新・なまず日記
2006-01-11 [水]
_ [AHK]AutoHotkeyのダメ文字問題対策
かなり遅くなってちょっとマヌケですが、みなさんあけましてオメデトございます。本年もなまず日記をよろしくお願いします。
さて、2006年最初の書き込みは、ちょっとしたライブラリを提供したいと思います。
AutoHotkeyは英語圏のソフトなので、日本語が混じった文字列を扱うと、いわゆる「ダメ文字問題」が発生します。例えば、ファイルのフルパスからファイル名を取り出すコマンドSplitPathで、 "c:\2006年度予算\研究材料予算.xls"というフルパスを処理してみましょう。
test := "c:\2006年度予算\研究材料予算.xls" SplitPath, test , OutFileName
OutFileNameにはファイル名が入るはずですが、結果は、"算.xls"となります。イヤになっちゃいますね。
ダメ文字問題がどうして起こるのか、ということについては、なまずより詳しい方が沢山いると思うので、ここでは概要だけ述べておきますと、
- 日本語(Shift-JIS)は2バイトで一文字なのに、AHKでは一文字を1バイトで取り扱う。
- 日本語(Shift-JIS)の2バイトのうちの後ろの1バイトで、英語に使う文字コードと同じコードが出てきてしまう。
といったところでしょうか。上記の例で言えば、"予算"の 予の字は、Shift-JISでは、0x975cという2バイトの文字コードであり、また、フォルダとファイル名の区切り文字である"\"は、0x5cという1バイトの文字コードなのです。英語圏のソフトは、予の字が2バイトで構成されていることを知らず、単に、"\"(0x5cという文字)を探しにいくので、予の字の下位1バイトを、"\"、つまりフォルダとファイル名の区切り文字であると勘違いしてしまい、上記のような悲惨な結果になる、というわけです。こういう場合、予は、ファイル名としては使えない、つまりダメ文字、と呼ぶらしいです。でも、そんなこといちいち考えてファイル名を考えなければいけないなんて、やってられないですよね。
以前、「AutoHotkeyを流行らせるページ」の掲示板に、「AHKってダメ文字あるよね。ソースコードの中で、1バイト文字列処理関数を使っているから、これを2バイト系に変えればいいんじゃないかな」という書き込みをしたら、「じゃ、よろしく(おまえがやれよ)」とのレスを頂きました。
確かに、いいだしっぺがなんとかするのが、この世界(?)のオキテであります。ヨロシクされた以上、なまずがなんとかしなければならないのですが、しかし、AHKのソースコードを一度いじってしまったら、なまずは一生、AHKにパッチをあて続けなければなりません。バージョンアップが頻繁なAHKにあわせてパッチをあて続けるのは、あまりやりたい作業ではありません。
もっとスマートな解決手段はないものかと考えて、DllCallを使って、日本語(マルチバイト文字列=Shift-JIS、もっと正確に言うと、Windows-31Jというんですか?詳しい方教えて下さい)処理ライブラリを作ってみました。ようするに、内部でワイドキャラ(UNICODE、UTF-16になるのかな?)に変換して、あれこれやって、またマルチバイト文字列に戻すわけです。AHKのオリジナルの文字列処理コマンドの代わりに、このライブラリで用意した関数をコールすれば、ダメ文字問題は解決されます。Sortコマンドを除くほとんどの文字列処理コマンドに対応しました。あと、ちょっとしたおまけ関数も追加しました。
上記でおわかりだとは思いますが、日本語処理ライブラリといっても、Shift-JIS(Windows-31J?)の範囲の漢字が文字化けなく扱えるようになる、というだけで、例えば「吉田さん」の吉の字の、上のところが土になっているような漢字が使えるようになるなんて、夢のような話では全然ありませんので、ご了承ください(あたりまえか)。
以下のファイルをダウンロードして、解凍した後、中の"mbstring.ahk"をインクルードしてお使いください。
mbstring_v00900.zip
以下、各関数の仕様を、AHKオリジナルのコマンドと対比する形で、説明します。
- InStrの代わり
pos := MBS_InStr( inputVar, searchText [, caseSnstv, startingPos ] )
引数:
- inputVar
検査する文字列が入った変数(オリジナルと同じ) - searchText
検索文字列(オリジナルと同じ) - caseSnstv
trueで、大文字小文字の区別をする。省略時はfalse (オリジナルと同じ) - startingPos
検査開始文字指定(オリジナルと同じ)
戻り値:
- pos
searchTextがinputVarの中で最初に出現する位置を返す。出現しなかったら0(= false)。(オリジナルと同じ)
- ダメ文字問題がない以外、オリジナルのInStrと同じ。全角文字も1文字と数える。
- inputVar
- If var is typeの代わり
result := MBS_IfVarIsType( var, type )
引数:
- var
検査する文字列が入った変数(オリジナルと同じ) - type
検索する型をあらわす文字列 オリジナルと同じ型が指定できるが、追加で以下の型も 指定できる。
- hankaku : varがすべて半角だったらtrue。半角カナも半角とみなす。
- zenkaku : varがすべて全角だったらtrue
戻り値:
- result
varがすべてtypeで指定した型だったらtrue,そうでなければfalseが返る(オリジナルと同じ)
動作:
- typeが"integer", "float", "number", "time"の場合、varがすべて半角だったら、オリジナルのIf var is typeをコールする。全角だったら、falseを返す。
- "hankaku", "zenkaku", "digit", "xdigit", "alpha", "upper", "lower", "alnum", "space"だったら、全角、半角を考慮した検査を行う。例えば、"digit"の場合、全角の123でも、trueが返る。また、"space"だった場合は、全角の空白もtrueが返る。
- それ以外は、オリジナルと同じ。
使用例:
if(MBS_IfVarIsType(test,"hankaku") MsgBox, test is hankaku. else MsgBox, test is not hankaku. - var
- Loop,PARSEの代わり
result := MBS_ParseBegin( inputVar [, delimiters, omitChars, mark ] )
引数:
- inputVar
分割される文字列が格納された変数名(オリジナルと同じ) - delimiters
区切り文字として使用したい文字を列挙。全角文字も指定できる。オリジナルと同じ意味だが、"CSV"は指定できない。 - omitChars
各フィールドの先頭と末尾から取り除きたい文字を列挙。全角文字も指定できる。 - mark
MBS_ParseReturn()で抜ける際、抜け出すループを指定する。(後述)
戻り値:
- result
成功時はtrue,メモリ取得に失敗した場合はfalse。 しかし、おそらくメモリ取得に失敗した場合は、AHK自体がまともに動かない だろうから、チェックはあまり意味がないと思う。
index := MBS_Parse( loopField [, method ] )
引数:
- loopField
切り取られた文字列が格納される。オリジナルのA_LoopFieldと同じ。 - method
おまけ機能。
1を指定すると、右端の文字列を切り取り、loopFieldに格納する。2を指定すると、切り取られた後の残りの文字列すべてをloopFieldに格納する。省略時は、通常通り、左端の文字列を切り取り、loopFieldに格納する。1指定時(右端切り取り時)は、左端指定時と同じように、内部に保存している文字列は短くなる。つまり、1を指定してMBS_Parse()をコールすれば、右端から、parseしていくことになる。2指定時(残り文字列取り出し時)は、切り取り動作は行わない。つまり、2を指定してMBS_Parse()を何度呼んでも、そのたびにloopFieldは同じ値が入り、変化しない。
戻り値:
- index
ループ回数。オリジナルのA_Indexと同じ。すべてParseし終わると、0が返る。その場合、loopFieldはNULLが格納される。
MBS_ParseEnd()
引数:
- なし
戻り値:
- なし
MBS_ParseReturn()
引数:
- なし
戻り値:
- なし
動作:
- Loop, PARSEと同様な動作をさせるための関数。以下の記述は、まったく同じ意味となる。
; オリジナル Loop, PARSE, inputVar1, `, { Loop, PARSE, inputVar2, `, { MsgBox, %A_LoopField% } }
; 本関数(上記のオリジナルと同じ意味) MBS_ParseBegin(inputVar1,",") Loop { if(!MBS_Parse(loopField)) break
MBS_ParseBegin(inputVar2,",") Loop { if(!MBS_Parse(loopField)) break
MsgBox, %loopField% } MBS_ParseEnd() } MBS_ParseEnd() - MBS_ParseBegin()と、MBS_ParseEnd()とは、必ずループの外に、セットで 存在しなければならない。メモリが許す限り、MBS_ParseBegin()で始まるループは、何回でも入れ子にすることができる。ただし、ループの外で、必ず対応するMBS_ParseEnd()をコールしなければならない(そうしないと、入れ子の外に出たことを認識できない)。
- オリジナルと同じように、inputVarの内容は、内部に取り込まれるので、 MBS_ParseBegin()の後で、自由に内容を変更してよい。
- MBS_ParseEnd()をコールせずに、一気に入れ子を脱出する場合(ループの中から、関数をreturnしてしまう場合など)は、MBS_ParseReturn()をコールする。すると、一番最近にmarkをtrueにしたMBS_ParseBegin()のループまでが、解消(内部に保持した文字列の廃棄)される。
; オリジナル Loop, PARSE, inputVar1, `, { Loop, PARSE, inputVar2, `, { if(A_LoopField == "end") return } }
; 本関数(上記と同じ意味) MBS_ParseBegin(inputVar1,",","",true) ; markをtrueに Loop { if(!MBS_Parse(loopField)) break
MBS_ParseBegin(inputVar2,",") Loop { if(!MBS_Parse(loopField)) break
if(loopField == "end") { MBS_Return() ; returnの前に、かならずMBS_Return()を呼ぶ ; このコールで、一番最近markをtrueにした ; ループ(この場合はinputVar1のループ)までが、 ; 解消される。 return } } MBS_ParseEnd() } MBS_ParseEnd() - MBS_Parse()のmethodの使い方は、以下を参照のこと。
test := "a,b,c,d"
MBS_ParseBegin(test,",")
index := MBS_Parse(loopField) ; loopField = "a" index = 1 index := MBS_Parse(loopField,1) ; loopField = "d" index = 2 index := MBS_Parse(loopField,2) ; loopField = "b,c" index = "" index := MBS_Parse(loopField) ; loopField = "b" index = 3 index := MBS_Parse(loopField) ; loopField = "c" index = 4 index := MBS_Parse(loopField) ; loopField = "" index = 0
MBS_ParseEnd() - オリジナルと異なり、delimiters に"CSV"を指定することができない。(多分)"CSV"では、ダメ文字問題が発生しないので、オリジナルのLoop,PARSEの方をお使いになることをお勧めする。
- inputVar
- StringLowerの代わり
MBS_StringLower( outputVar, inputVar [, title ] )
引数:
- outputVar
変換後の文字列を格納する変数名(オリジナルと同じ) - inputVar
変換もとの文字列の入った変数の名前 - title
"T"(ほんとは、NULL以外を指定すればなんでもいい)を指定すると、単語の先頭だけが大文字で後は小文字の形式に変換される。
戻り値:
- なし
動作:
- 英字を小文字にする。全角の英字も小文字になる。実は、titleを指定した時以外は、標準のStringLowerを呼んでいる。titleを指定した場合は、標準ではダメ文字問題が出るので、この関数で処理している。
使用例:
; inputVarを、"Title Format"(this を、Thisに)にして、outputVarに MBS_StringLower(outputVar, inputVar, "T") - outputVar
- StringUpperの代わり
MBS_StringUpper( outputVar, inputVar [, title ] )
引数:
- outputVar
変換後の文字列を格納する変数名(オリジナルと同じ) - inputVar
変換もとの文字列の入った変数の名前 - title
"T"(ほんとは、NULL以外を指定すればなんでもいい)を指定すると、単語の先頭だけが大文字で後は小文字の形式に変換される。
戻り値:
- なし
動作:
- 英字を大文字にする。全角の英字も大文字になる。実は、titleを指定した時以外は、標準のStringUpperを呼んでいる。titleを指定した場合は、標準ではダメ文字問題が出るので、この関数で処理している。
- outputVar
- StringLeftの代わり
MBS_StringLeft( outputVar, inputVar , count )
引数:
- outputVar
抜き出した文字列を格納する変数名(オリジナルと同じ) - inputVar
抜き出す元の文字列の入った変数名(オリジナルと同じ) - count
抜き出す文字数(オリジナルと同じ)
戻り値:
- なし
動作:
- count は、全角も1文字と数える。それ以外は、オリジナルと同じ
- outputVar
- StringRightの代わり
MBS_StringRight( outputVar, inputVar , count )
引数:
- outputVar
抜き出した文字列を格納する変数名(オリジナルと同じ) - inputVar
抜き出す元の文字列の入った変数名(オリジナルと同じ) - count
抜き出す文字数(オリジナルと同じ)
戻り値:
- なし
動作:
- count は、全角も1文字と数える。それ以外は、オリジナルと同じ
- outputVar
- StringTrimLeftの代わり
MBS_StringTrimLeft( outputVar, inputVar , count )
引数:
- outputVar
抜き出した文字列を格納する変数名(オリジナルと同じ) - inputVar
抜き出す元の文字列の入った変数名(オリジナルと同じ) - count
抜き出す文字数(オリジナルと同じ)
戻り値:
- なし
動作:
- count は、全角も1文字と数える。それ以外は、オリジナルと同じ
- outputVar
- StringTrimRightの代わり
MBS_StringTrimRight( outputVar, inputVar , count )
引数:
- outputVar
抜き出した文字列を格納する変数名(オリジナルと同じ) - inputVar
抜き出す元の文字列の入った変数名(オリジナルと同じ) - count
抜き出す文字数(オリジナルと同じ)
戻り値:
- なし
動作:
- count は、全角も1文字と数える。それ以外は、オリジナルと同じ
- outputVar
- StringMidの代わり
MBS_StringMid( outputVar, inputVar , startChar, count [, dir ] )
引数:
- outputVar
抜き出した文字列を格納する変数名(オリジナルと同じ) - inputVar
抜き出す元の文字列の入った変数名(オリジナルと同じ) - startChar
取り出す部分の開始位置(オリジナルと同じ) - count
抜き出す文字数(オリジナルと同じ) - dir
"L"(実はNULLでなかったらなんでもいい)を指定すると、StartChar以前(左)の部分をCount文字だけ取り出す(オリジナルと同じ)
戻り値:
- なし
動作:
- startChar やcount は、全角も1文字と数える。それ以外は、オリジナルと同じ
- outputVar
- StrLenの代わり
len := MBS_StrLen( var )
引数:
- var
検査する文字列
戻り値:
- len
文字列の長さ
動作:
- len は、全角も1文字と数える。それ以外は、オリジナルと同じ
- var
- StringGetPosの代わり
pos := MBS_StringGetPos( inputVar, searchText [, dir , offset, caseSnstv ] )
引数:
- inputVar
検査する文字列の入った変数名(オリジナルと同じ) - searchText
検索する文字列(オリジナルと同じ) - dir
"1"とか、"L2"とか、"R3"とか指定する。省略時は"L1"と同じ(オリジナルと同じ) - offset
検索時、最初のOffset文字分だけ無視して検索を始める。省略時は"0"と同じ(オリジナルと同じ) - caseSnstv
trueのとき、大文字小文字を区別する。省略時は区別しない。
戻り値:
- pos
検索された位置。文字列の最初は「0」として数える。(オリジナルのoutputVarと同じ)。offsetがマイナスだったり、dirLRの値が上記定義以外だったり、inputVarかsearchTextがNULLだった場合は、-2が返る。
動作:
- pos は、全角も1文字とみなす。それ以外は、オリジナルと同じ
- inputVar
- StringReplaceの代わり
err := MBS_StringReplace( outputVar, inputVar, searchText [, replaceText, replaceAll, caseSnstv ] )
引数:
- outputVar
置換結果の文字列を格納する変数(オリジナルと同じ) - inputVar
置換前の文字列を格納している変数(オリジナルと同じ) - searchText
検索する文字列(オリジナルと同じ) - replaceText
searchTextが置き換えられる先の文字列(オリジナルと同じ) - replaceAll
省略するか、"1"なら、最初の一回だけ、置換する。それ以外なら、すべての文字列を置換する。 - caseSnstv
trueのとき、大文字小文字を区別する。省略時は区別しない。
戻り値:
- err
置換回数を返す。-1のときは、メモリが取得できなかったことを示す。
動作:
- ダメ文字問題がない以外は、オリジナルと同じ
- outputVar
- StringSplitの代わり、というか、手伝い。
dlmt := MBS_StringSplit( outputVar, inputVar [, delimiters, omitChars, outputVarDelimiter ] )
引数:
- outputVar
分割(?)結果の文字列を格納する変数 - inputVar
分割前の文字列を格納している変数(オリジナルと同じ) - delimiters
区切り文字として使用したい文字を列挙(オリジナルと同じ)。全角文字も使える。 - omitChars
分割された各要素の最初と最後から取り除く文字を列挙 (オリジナルと同じ)。全角文字も使える。 - outputVarDelimiter
outputVarに格納される文字列の区切り文字を指定する。省略時には、区切り文字は、0x01というコードが使われる。
戻り値:
- dlmt
outputVar に格納される文字列の区切り文字が返る。outputVarDelimiter を省略したら、常に0x01。outputVarDelimiter を指定したら、常に、outputVarDelimiter と同じ値。
動作:
- オリジナルのStringSplitは、分割した各フィールドが、 array1, array2 といった疑似配列変数に格納されるが、関数では、そのような動作は実現できない。しかし、オリジナルのStringSplitでは、ダメ文字問題が発生してしまう。
- そこで、本関数では、ダメ文字問題が発生しない形で inputVar を分割し、それをShift-JISでもASCIIでも使わない0x01というコードを区切り文字とする一つの文字列として、 outputVar に格納する。0x01で分割する分には、少なくともShift-JISに関しては、ダメ文字問題は絶対に起こらないので、オリジナルのStringSplitを使えばよい。
-
以下のコードは、まったく同じ意味となる。
; オリジナル inputVar := "a,b,c" StringSplit, array, inputVar, `,
; 本関数 inputVar := "a,b,c" dlmt := MBS_StringSplit(tmp, inputVar, ",") StringSplit, array, tmp, %dlmt% - 区切り文字0x01は、Shift-JISでもASCIIでも通常は絶対に使わない文字だが、もし、 inputVar の中で、あえてこのコードを使っていると、当然、分割時に誤動作する。そこで、そのような場合は、 outputVarDelimiter で、任意の文字を指定する必要がある。ダメ文字問題を起さないためには、その時指定する文字は、0x3e以下の値である必要がある。
- outputVar
- SplitPathの代わり
MBS_SplitPath( InputVar , outFileName, outDir, outExtension, outNameNoExt, outDrive )
引数:
- inputVar
分解するファイルパスを格納した変数 - outFileName
オリジナルと同じ。ただし、省略は不可 - outDir
オリジナルと同じ。ただし、省略は不可 - outExtension
オリジナルと同じ。ただし、省略は不可 - outNameNoExt
オリジナルと同じ。ただし、省略は不可 - outDrive
オリジナルと同じ。ただし、省略は不可
戻り値:
- なし
動作:
- ダメ文字問題がない以外は、オリジナルと同じ。ただし、outFileNameなどは、ByRefを使っているので、省略できないのが残念なところ。必要のない変数に関しては、dummy とか、適当な変数を指定しておいてください。
- inputVar
- 半角文字を、対応する全角文字に変換する。
MBS_StringZenkaku( outputVar, inputVar [, alpha, num, symbol, space, kana ] )
引数:
- outputVar
変換後の文字列を格納する変数 - inputVar
変換前の文字列を格納する変数 - alpha
英字を変換する場合はtrue,しない場合はfalse。省略時はtrue - num
数字を変換する場合はtrue,しない場合はfalse。省略時はtrue - symbol
記号変換する場合はtrue,しない場合はfalse。省略時はtrue - space
半角空白(0x20)を、全角空白に変換するときはtrue,しない場合はfalse。省略時はtrue - kana
半角カナを全角カナに変換するときはtrue,しない場合はfalse。省略時はfalse ← 注意。ここだけデフォルトはfalse
戻り値:
- なし
動作:
- いわゆるASCII文字列(0x20から0x7e)を、対応する全角文字に変換する。
- また、半角カナも、対応する全角カナに変換する。その場合、半角カナの"バ"などは、ハとテンテンの二文字は一つの全角文字になるので注意。
- 「対応する全角文字に変換」といっても、なまずが似てる文字を適当に選んでいるので、厳密なものではありません。「こんな変換はねぇだろう」というのがありましたら、是非お知らせください。
使用例:
; inputの半角文字列を変換できるものはすべて全角に MBS_StringZenkaku(output,input,true,true,true,true,true) - outputVar
- 全角文字を、対応する半角文字に変換する。
MBS_StringHankaku( outputVar, inputVar [, alpha, num, symbol, space, kana ] )
引数:
- outputVar
変換後の文字列を格納する変数 - inputVar
変換前の文字列を格納する変数 - alpha
英字を変換する場合はtrue,しない場合はfalse。省略時はtrue - num
数字を変換する場合はtrue,しない場合はfalse。省略時はtrue - symbol
記号変換する場合はtrue,しない場合はfalse。省略時はtrue - space
全角空白を、半角空白(0x20)に変換するときはtrue,しない場合はfalse。省略時はtrue - kana
全角カナを半角カナに変換するときはtrue,しない場合はfalse。省略時はfalse ← 注意。ここだけデフォルトはfalse
戻り値:
- なし
動作:
- 全角文字を、いわゆるASCII文字列(0x20から0x7e)に、変換する。
- また、全角カナも、対応する半角カナに変換する。その場合、全角カナの"バ"などは、一つの全角文字が、ハとテンテンの二つの半角文字になるので注意。
- 「対応する半角文字に変換」といっても、なまずが似てる文字を適当に選んでいるので、厳密なものではありません。「こんな変換はねぇだろう」というのがありましたら、是非お知らせください。
- outputVar
さて、文字列操作コマンドのダメ文字問題は、上記の関数でなんとかなるのですが、それ以外にも、AHKには、ダメ文字問題があります。例えば、チンポとMsgBoxで出力してみてください。
MsgBox, チンポ
表示は、ャ塔|となります。なんだこれ?
別に、チンポが下品だからAHKが伏字にした、というわけではないのです。AHKは、デフォルトでは、"`"(バッククオート)が、エスケープ文字になっています。AHKは、スクリプト中の文字列の"`"を探し、多くの場合、これを削除してしまいます。そして、"`"の文字コードは0x60、"チンポ"のチのShift-JIS文字コードは0x8360なので、AHKはチの2バイト目を削除してしまうのです。すると、残った1バイト目の0x83とンの1バイト目の0x83がくっついて0x8383、つまりャになってしまい、ンの2バイト目とポの1バイト目がくっついて塔となり...といったことがドミノ倒しのように重なって、上記のような結果になるわけです。チンポと表示できないなんて、困りましたね(←いや、チンポはべつに困らないと思うぞ)
でも、この問題の対処は、とても簡単です。AHKには、#EscapeCharという、エスケープ文字を変更するコマンドがあります。これをつかって、エスケープ文字を、"`"(バッククオート)のような、ダメ文字問題が起こる文字ではなく、Shift-JISでは絶対使わない、0x3e以下の、ダメ文字問題が起きない文字に変更してしまえばいいのです。例えば、"'"(シングルクオート)などです。以下のコードなら、正しくチンポと表示されます。
#EscapeChar ' ; シングルクオートにエスケープ文字を変更
MsgBox, チンポ
上記のようなことがあるので、汎用的なライブラリを作るような場合は、エスケープ文字を使うのは控えた方がいいですね。なぜなら、汎用的なライブラリを利用する親ルーチンの方で、#EscapeCharでエスケープ文字を変更してしまったら、エスケープ指定が無効になり、あっという間にバグありコードになってしまうからです。また、汎用的なライブラリで、不用意に、エスケープ文字に指定されそうな、記号を文字列として持つのも、好ましくありません。本ライブラリも、Chr()を使って、文字列中に記号を指定しないようにしています。
col := Chr(0x3a) ; ":" sla := Chr(0x2f) ; "/" url := "http" . col . sla . sla
かくいうなまずも、この事実に気がついたのは最近のことで、汎用的に使おうと思っていたサブルーチンでバンバンエスケープ文字を使ってしまっていて、トホホ、と思っているところです。これからライブラリを書こうという皆さん、ご注意くださった方がいいと思いますよ。
さてさて、ダメ文字問題対策、いかがでしたでしょうか。AHKプログラマの皆さんにとって、必ず、本ライブラリが必要か、というと、そういうものではないでしょう。AHKというスクリプト言語の性格上、必ずしも日本語処理が必須、ということはないし、作ろうとするスクリプトの仕様によっては、オリジナルの文字列処理コマンドで、ダメ文字問題のない、日本語処理も可能です(なまずIMEも、オリジナルの文字列処理コマンドしか使っていません)。また、当然ながら、本ライブラリよりも、オリジナルの文字列処理コマンドの方が、効率が良く、処理速度が速いにきまっています。
しかし、なまずIMEの時もそうでしたが、ダメ文字問題を考えながらプログラムを作るのは、結構気を使うし、うっとうしいものです。本当に処理したい問題があるのに、あまり本質的でない、ダメ文字を気にするのは、気分の良いものではありません。そういうとき、とりあえず、本ライブラリを使って、本当に処理したい問題が解決できるコードを作ってしまって、処理速度などの問題がでたら、それから、問題のない範囲で、オリジナルの文字列処理コマンドに戻す、そんな使い方をしていただけたら、なまずとしては望外の幸せであります。つまらない問題はライブラリにまかせて、おもしろいプログラミングを楽しもうではありませんか、ご同輩。
また、他の人にAHKを紹介するときでも、「AHKっていいよぉ〜、大抵の処理ができちゃうよ。......日本語処理はちょっとダメだけどね」と言うよりも、「日本語処理も、なまずってバカが作ったライブラリを使えば、なんとかなるみたいよ」などと言ったほうが、ウケが良いのではないでしょうか。そんな感じで、AHKの普及のほんの少しでも力になれたら、なまずとしては大感激であります。
長い記事を読んでいただき、ありがとうございました。今年もなまず日記をよろしくお願いします。