ファイルの自動分割保存No.11648
Alter Ego さん 24/05/19 18:31
 
数万行のテキストファイルを5000行ごとか1000kb未満の行に自動で分割し保存してく
れる機能は秀丸本体にありますか?

DivideFile.macというのがあったので試してみたのですが、対象がUTF-16の文字を使
うファイルだったせいか、
「文字コード変換できない文字が含まれていたので、?マークや同義の文字などに変
換して保存しました。
読み込みし直しますか?」
となって正常に動作しません。


[ ]
RE:11648 ファイルの自動分割保存No.11649
こみやんま さん 24/05/20 00:55
 
何行とかで分ける、といったのは秀丸マクロ(JS含む)である程度容易ですが、
「キロバイトで分ける」というのは、少し難しいように思えますね。
(単純にサイズで分割すると、分割したところが文字を構成するバイトの途中だとヤ
バいので)
(ちょっとC++なりC#なり使わないと速度的にも厄介かも)

50万行程度までなら、大きなファイルでも指定の行で分割するのは、
下記の秀丸マクロでいけると思いますね。
(実行した瞬間終わってるくらいほとんど一瞬だと思います)


jsmode "WebView2";
js {

try {
    // ファイル名無しは対象にしない
    if (!hidemaru.getFileFullPath()) {
        throw "ファイル名が付いているファイルのみ対象とします。";
    }

    function getPerChunk() {
        let answer = input("何行で分けますか?");
        let linesPerChunk = 5000;
   
        try {
            linesPerChunk = Number(answer);
        } catch (err) { }
        return linesPerChunk;
    }
   
    // 対象のテキストを該当の行数で割って配列にして返す
    function splitText(text, linesPerChunk) {
        const chunks = [];
        const textArray = text.split('\n');

        for (let i = 0; i < textArray.length; i += linesPerChunk) {
            chunks.push(textArray.slice(i, i + linesPerChunk).join('\n'));
        }

        return chunks;
    }

    function getFilePathInfo(filePath) {

        // '.' でファイル名を拡張子とそれ以外に分割して配列に格納
        var parts = filePath.split('.');

        // 拡張子とそれ以外を別々の変数に格納
        var ext = parts.pop(); // 拡張子
        var name = parts.join('.'); // 拡張子以外の部分

        return {
            name: name,
            ext: ext
        };
    }

    function saveChunksToFiles(chunks) {

        let cp = codepage();
        let hasbom = bom();
   
        function getTargetEncode() {
            switch (cp) {
                case 932: return "sjis";
                case 1200: return "utf16";
                case 65001: return hasbom ? "utf8bom" : "utf8";
            }
   
            return "utf8";
        }
   
        let fname = hidemaru.getFileFullPath();
        let maxFormatLength = chunks.length.toString().length;
        for (let i = 0; i < chunks.length; i++) {
            let pathAttr = getFilePathInfo(fname);
            // 配列の個数を文字列化したその文字列の長さに足りない部分を0埋め
すればよい。
            // 配列の個数が30個なら、"30"という文字列の長さ、すなわち2文字。
            // 現在の数値が3なら3を文字列化して足りない文字数すなわち1つが0
で埋まる
            const zeroFormatNumber = (i + 1).toString().padStart(maxFormatLe
ngth, '0');
   
            // ファイル名を新たに分割したファイル名に
            let newFileName = `${pathAttr.name}_${zeroFormatNumber}.${pathAt
tr.ext}`;
            hidemaru.saveTextFile(newFileName, chunks[i], getTargetEncode());
        }
    }

    function main() {
        // 何行に分割するかを得る
        let linesPerChunk = getPerChunk();
        // 現在のテキストを取得
        const text = hidemaru.getTotalText();
        // テキストを分割して配列に格納
        const chunks = splitText(text, linesPerChunk);
        // 分割したテキストを分割してファイルに保存
        saveChunksToFiles(chunks);
    }

    main();
   
} catch (err) {
    let dll = loaddll("HmOutputPane.dll");
    dll.dllFunc.Output(hidemaru.getCurrentWindowHandle(), err + "\r\n");
}


} // js

[ ]
RE:11649 ファイルの自動分割保存No.11650
こみやんま さん 24/05/20 01:07
 
実行中にencodeが変化することはないので、
saveChunksToFiles の中身は

    function saveChunksToFiles(chunks) {

        let cp = codepage();
        let hasbom = bom();
   
        function getTargetEncode() {
            switch (cp) {
                case 932: return "sjis";
                case 1200: return "utf16";
                case 65001: return hasbom ? "utf8bom" : "utf8";
            }
   
            return "utf8";
        }
   
        let fname = hidemaru.getFileFullPath();
        let maxFormatLength = chunks.length.toString().length;

        let enc = getTargetEncode();
        for (let i = 0; i < chunks.length; i++) {
            let pathAttr = getFilePathInfo(fname);
            // 配列の個数を文字列化したその文字列の長さに足りない部分を0埋め
すればよい。
            // 配列の個数が30個なら、"30"という文字列の長さ、すなわち2文字。
            // 現在の数値が3なら3を文字列化して足りない文字数すなわち1つが0
で埋まる
            const zeroFormatNumber = (i + 1).toString().padStart(maxFormatLe
ngth, '0');
   
            // ファイル名を新たに分割したファイル名に
            let newFileName = `${pathAttr.name}_${zeroFormatNumber}.${pathAt
tr.ext}`;
            hidemaru.saveTextFile(newFileName, chunks[i], enc);
        }
    }

の方がいいですね。
ループはぜいぜい500以下でしょうから体感するような差は発生しないでしょうけれ
ども。

[ ]
RE:11648 ファイルの自動分割保存No.11652
こみやんま さん 24/05/20 11:56
 
>「1000kb未満の行」に自動で分割し保存してくれる機能は秀丸本体にありますか?


>> 「1000kb未満の行に自動で分割」し保存してくれる機能は秀丸本体にありますか?

これについて、作成してみましたが、やはりスクリプトでは速度がでませんでした。
(たとえ.NET Frameworkのライブラリをスクリプトから直接使用しても、2Kの100ファ
イル程度で30秒はかかる)
素の秀丸マクロだとおそらくまともに組めない。

一方、C#でネイティブコンパイル & 並列保存するようにしたところ、結構な速さと
なりました。
(ちょっと自分で想定していたよりもかなり速かったです。数千ファイル分割でも、
なお数秒以内でしょう)

■場所
 └ https://github.com/komiyamma/hm_split_textfile_bysize

■リリースファイルの場所
 └ https://github.com/komiyamma/hm_split_textfile_bysize/releases/latest

■どれをダウンロードするの?
ブラウザやアンチウィルスソフトによっては、特定のアーカイブ(特にexe入りのzip)
にはかなりきつい反応を示すため、
ファイルを(.zip、.7z、.tar)の3つを用意しておきました。

どれも同じものです。


■「改行を考慮する、しない」をマクロから切替

HmSplitTextFileBySize.mac 内の

「#bLineConsider = 0;」



「#bLineConsider = 1;」

とすれば、「改行」を考慮するようになります。

だたし、そもそも本文内に「1つたりとも改行がない」といったこともあります。
よって、まずは「サイズ(????キロバイト)」でカットされ、
続いて「改行があれば」そのサイズ未満を満たした改行でカットされます。


■ファイル分割の境界線に合成文字など
まぁ常識的な範囲だと上手くいくでしょうが、あまりにもすんごいのはダメでしょう
きっと
(完全に判定しようとすると、きっとものすごい遅くなる)

[ ]
RE:11652 ファイルの自動分割保存No.11654
Alter Ego さん 24/05/24 08:54
 
昨晩試してみました。
目的の動作がすごくシンプルにできて非常に助かります。
ありがとうございました。

[ ]