単バイトでない文字で構成された文字列の解析

元々charで表現できる文字だけで構成された文字列にマーク付けしたものを解析することを目的にしているわけで、
ASCIIだろうとEBCDICだろうとそれが前提の環境上でコンパイル、実行できると思うが、
charの範囲で表現できないような文字が使われている場合には鼻から悪魔である。
ということで、うまくいくときにはうまくいく。
例えば、ASCII前提の環境で、

$ echo -n "(出鼻)を挫く" | iconv -t Shift_JIS | ./parser | iconv -f Shift_JIS
marked-string [出鼻]
string [を挫く]

シフトJISで符号化された文字列をparserに食わせているが特に問題なく期待した結果が得られる。
しかし、マーク開始文字をデフォルトの'('から'@'に変えた入力だと、

$ echo -n "((@@出鼻)を挫く" | iconv -t Shift_JIS | ./parser | iconv -f Shift_JIS
marked-string [出呆挫]
string [ュ]

解析は完了したが文字が化けた。
マーク終了文字の方を')'から'@'に変えると、

$ echo -n "()@(出鼻@を挫く" | iconv -t Shift_JIS | ./parser | iconv -f Shift_JIS
syntax error
marked-string [出評

文字化けどころか解析そのものに失敗する。
これはメタ文字を変更すると常に起きるというわけではない。

$ echo -n "(出端)を挫く" | iconv -t Shift_JIS | ./parser | iconv -f Shift_JIS
marked-string [出端]
string [を挫く]

$ echo -n "((@@出端)を挫く" | iconv -t Shift_JIS | ./parser | iconv -f Shift_JIS
marked-string [出端]
string [を挫く]

$ echo -n "()@(出端@を挫く" | iconv -t Shift_JIS | ./parser | iconv -f Shift_JIS
marked-string [出端]
string [を挫く]

今度はどの場合でも期待した結果が得られた。
この原因は「鼻」の字にある。
シフトJISにおける「鼻」の2バイト目は64、これは'@'と同じ値である。
parserがこの2バイト目をメタ文字の@として扱ってしまうことがこうした現象を引き起こす。

デフォルトのメタ文字の'('')'':'はそれぞれASCIIで40、41、58であり、
シフトJISの2バイトコードの1バイト目*1にも2バイト目*2にも出現しないため、
こうしたメタ文字に関するparserの混乱は起こらないと考えられる。
が、メタ文字を変更すると変更後の文字次第では衝突が起こる文字列ができてしまう。

parserに入力するコードをシフトJISの替わりにUTF-8にすると、
ASCII文字((127以下))と同じバイトが現れるのはまさにそのASCII文字以外にはないので、
メタ文字をデフォルトから変えてもシフトJISで起きるような問題は起きないと考えられる。

$ echo -n "(出鼻)を挫く" | iconv -t UTF-8 | ./parser | iconv -f UTF-8
marked-string [出鼻]
string [を挫く]

$ echo -n "((@@出鼻)を挫く" | iconv -t UTF-8 | ./parser | iconv -f UTF-8
marked-string [出鼻]
string [を挫く]

$ echo -n "()@(出鼻@を挫く" | iconv -t UTF-8 | ./parser | iconv -f UTF-8
marked-string [出鼻]
string [を挫く]

ともあれ、本来の使い方から外れた入力なのでどうなっても気にしないのがいいと思う。

*1:129以上

*2:64以上