JScript

【レガシー】正規表現でテキストファイルの語句を置換するスクリプト(コマンド) UTF-8対応

More than 1 year has passed since last update.

テキストファイル中の語句を正規表現で置換する Windows 向けのコマンドです。

もとは2012年に公開したものです。他にも PowerShell を使うとか、Git Bash などを導入してテキスト処理系の UNIX コマンドを使うとか、その他の置換用のフリーウェアを使うという手もあったのではと思います。JScript にした理由はどうあれ、バッチ処理による自動化がしたかったようです。

ADODB.Stream を使用していて、UTF-8 入出力対応です。BOM なしでの出力もできます。スクリプト後半はテキストファイルの入出力メソッドになっていますので、同じく BOM なしの UTF-8 を出力したい方がいれば流用可能かと思います。

ウェブ上のサンプルを組み合わせつつ作られたものなので、このスクリプトも自由に改変して利用して頂ければと思います。

ソースコード

/** 
 * @fileOverview テキストファイルを置換する JScript コマンドです。
 * コマンド書式の詳細は、引数を付けずに起動することで表示されます。
 */

/**
 * WshArguments を JavaScript 標準の配列に変換する。
 */
function WshArgumentsToArray(wshArguments)
{
    var arguments = [];
    for (var i = 0; i < wshArguments.length; i++)
    {
        var arg = wshArguments(i);

        // WSHの引数にはダブルクオーテーションを利用することができない
        // 代替手段として、「`」が渡された場合「"」として扱う
        // また、「\`」が渡された場合は「`」として扱う
        arg = arg.replace(/^`/g, "\"");
        arg = arg.replace(/([^\\])`/g, "$1\"");
        arg = arg.replace(/\\`/g, "`");

        arguments.push(arg);
    }
    return arguments;
}

/**
 * コマンド メイン処理
 * @param {Array} arguments 引数文字列を格納した配列
 */
function Main(arguments)
{
    var EXIT_SUCCESS = 0;
    var EXIT_FAILURE = 1;
    var ERROR_TEXT_TOO_FEW_PARAMETERS = "パラメータが少なすぎます";

    if (arguments.length == 0)
    {
        // 引数がなければ使い方を表示して終了する
        ShowUsage();
        return EXIT_SUCCESS;
    }

    // 読込先文字コード
    var charsetDst = "utf-8n";
    // 書込元文字コード
    var charsetSrc = "_autodetect";
    // 読込先ファイル名
    var filenameDst = "";
    // 書込元ファイル名
    var filenameSrc = "";
    // 置換先キーワード
    var keywordDst = "";
    // 置換元キーワード
    var keywordSrc = "";

    // オプション文字列を処理
    while (arguments.length > 0 && arguments[0].charAt(0) == "-")
    {
        var option = arguments.shift().substr(1);
        if (option.charAt(0) == "-")
        {
            // 長いオプション --long
            option = option.substr(1);
            if (option == "charset")
            {
                // 読込元/書込先文字コード指定
                if (arguments.length < 1)
                {
                    WScript.Echo(ERROR_TEXT_TOO_FEW_PARAMETERS);
                    return EXIT_FAILURE;
                }
                charsetDst = arguments.shift();
                charsetSrc = charsetDst;
                arguments.shift();
            }
            else if (option == "charset-dst")
            {
                // 読込元文字コード指定
                if (arguments.length < 1)
                {
                    WScript.Echo(ERROR_TEXT_TOO_FEW_PARAMETERS);
                    return EXIT_FAILURE;
                }
                charsetDst = arguments.shift();
            }
            else if (option == "charset-src")
            {
                // 書込先文字コード指定
                if (arguments.length < 1)
                {
                    WScript.Echo(ERROR_TEXT_TOO_FEW_PARAMETERS);
                    return EXIT_FAILURE;
                }
                charsetSrc = arguments.shift();
            }
            else
            {
                WScript.Echo("不明なオプションです: --" + option);
                return EXIT_FAILURE;
            }
        }
        else
        {
            // 1文字連続指定オプション -AbCd
            if (option.length == 0)
            {
                WScript.Echo("オプション指定が無効です");
                return EXIT_FAILURE;
            }
            for (var i = 0; i < option.length; i++)
            {
                var optionChar = option.charAt(i);
                if (optionChar == "")
                {
                    // パラメータ処理があれば追加...
                }
                else
                {
                    WScript.Echo("不明なオプションです: -" + optionChar);
                    return EXIT_FAILURE;
                }
            }
        }
    }

    // 読込元ファイル名
    if (arguments.length < 1)
    {
        WScript.Echo("読込元ファイル名が指定されていません");
        return EXIT_FAILURE;
    }
    filenameSrc = arguments[0];

    // 書込先ファイル名
    if (arguments.length < 2)
    {
        WScript.Echo("書込先ファイル名が指定されていません");
        return EXIT_FAILURE;
    }
    filenameDst = arguments[1];

    // 置換元キーワード
    if (arguments.length >= 3)
    {
        keywordSrc = arguments[2];
    }

    // 置換先キーワード
    if (arguments.length >= 4)
    {
        keywordDst = arguments[3];
    }

    // テキスト置換処理メイン
    var text = LoadTextFile(filenameSrc, charsetSrc);
    var regex = new RegExp(keywordSrc, "gm");
    text = text.replace(regex, keywordDst);
    SaveTextFile(filenameDst, text, charsetDst);
}

/**
 * コマンドの使用方法を出力する。
 */
function ShowUsage()
{
    var lineSeparator = "\n";
    WScript.Echo(
    "コマンド書式:" + lineSeparator +
    "  (cscript) TextReplace.js <引数> 読込元ファイル名 書込先ファイル名 置換元キーワード(正規表現) 置換後キーワード" + lineSeparator +
    "  ※正規表現中のパターンに一致したキーワードは $1,$2... で表現可能です。" + lineSeparator +
    "" + lineSeparator +
    "引数:" + lineSeparator +
    "--charset 読込元/書込先ファイルの文字コード" + lineSeparator +
    "  --charset-dst 書込先ファイルの文字コード (default: utf-8n)" + lineSeparator +
    "  --charset-src 読込元ファイルの文字コード (default: _autodetect)" + lineSeparator +
    "" + lineSeparator +
    "  charset の値の例:" + lineSeparator +
    "  _autodetect, euc-jp, iso-2022-jp, shift_jis, unicode, utf-8,..."
    );
}

//--[ テキスト入出力機能 開始 ]-------------------------------------------------

// StreamTypeEnum Constants
var adTypeBinary = 1;
var adTypeText = 2;

// LineSeparatorEnum Constants
var adLF = 10;
var adCR = 13;
var adCRLF = -1;

// StreamWriteEnum Constants
var adWriteChar = 0;
var adWriteLine = 1;

// SaveOptionsEnum Constants
var adSaveCreateNotExist = 1;
var adSaveCreateOverWrite = 2;

// StreamReadEnum Constants
var adReadAll = -1;
var adReadLine = -2;

/**
 * テキストファイルを読み込む。
 * @param {String} filename 読み込むファイルのパス
 * @param {String} charset 文字コード
 * @return {String} 文字列
 */
function LoadTextFile(filename, charset)
{
    // 文字コードの指定なき場合、自動検出とする。
    // (誤判定が起きることもあるので注意)
    if (charset == undefined)
    {
        charset = "_autodetect";
    }

    var stream, text;
    stream = new ActiveXObject("ADODB.Stream");
    stream.type = adTypeText;
    stream.charset = charset;
    stream.open();
    stream.loadFromFile(filename);
    text = stream.readText(adReadAll);
    stream.close();
    return text;
}

/**
 * テキストファイルを書き出す。
 * @param {String} filename 書き出すファイルのパス
 * @param {String} text 書き出すテキスト
 * @param {String} charset 文字コード
 */
function SaveTextFile(filename, text, charset)
{
    // 文字コードの指定なき場合、UTF-8Nとする。
    if (charset == undefined)
    {
        charset = "utf-8n";
    }

    // 独自にBOM除去を行う文字コードの判定
    var trimUnicodeBOM = false;
    var unicodeBOMLength = 0;
    if (charset.match(/^utf-8n$/i))
    {
        charset = charset.replace(/n$/i, "");
        unicodeBOMLength = 3;
        trimUnicodeBOM = true;
    }
    else if (charset.match(/^utf-16n$/i))
    {
        charset = charset.replace(/n$/i, "");
        unicodeBOMLength = 2;
        trimUnicodeBOM = true;
    }

    var stream;
    stream = new ActiveXObject("ADODB.Stream");
    stream.type = adTypeText;
    stream.charset = charset;
    stream.open();
    stream.writeText(text);
    if (trimUnicodeBOM)
    {
        // Unicode BOMを除去して出力

        // バイナリモードにするためにPositionを一度0に戻す
        // Readするためにはバイナリモードにする必要がある
        stream.Position = 0;
        stream.Type = adTypeBinary;
        // Positionを進めてから読み込むことで
        // BOMをスキップする
        stream.Position = unicodeBOMLength;
        // 変換後の内容をバッファに読み込む
        var textBinary = stream.Read();
        stream.Close();

        // 読み込んだバイナリデータを再出力する
        stream = new ActiveXObject("ADODB.Stream");
        stream.Type = adTypeBinary;
        stream.Open();
        stream.Write(textBinary);
    }
    stream.saveToFile(filename, adSaveCreateOverWrite);
    stream.close();
}

//--[ テキスト入出力機能 終了 ]-------------------------------------------------

WScript.Quit(Main(WshArgumentsToArray(WScript.Arguments)));

コマンド利用例

cscript //nologo TextReplace.js --charset-src utf-8 --charset-dst utf-8 読込元.txt 書込先.txt まぎわらしい まぎらわしい