Javaとエスケープ文字
メモです。
文字エスケープの処理ステップ
Javaでは特殊な文字コードのためのエスケープ表記が次の3つの 段階で処理されます。
- Java構文解析前のプリプロセス
- Java構文解析
- 関数内処理
1. Java構文解析前のプリプロセス: \u000a
Javaコンパイルでは構文解析の前にUnicodeのエスケープ文字表記 の展開が行われます。
Javaの構文とは何の関係も持たず、文字列の中だろうが コメントのなかだろうがとにかく"\u"を見つけたら所構わず 変換しようとします。(\\uは変換対象としません)
例えば次の文
System.out.print("Hello World\u000a");
は、Java構文解析の前に\u000aが改行に置き換えられ
System.out.print("Hello World
");
と解釈されてしまうため、
「文字列リテラルが閉じられていません」
と
いうエラーになります。
このプリプロセッサは\uで始まりUnicodeを構成できないパターン
に出会うとエラーとなります。
例えば次のプログラム
// D:\test\unit\Xtest.java
public class Xtest {
public static void main(String[] args_){
System.out.printf("Hello World%n");
}
}
は、先頭のコメントの中の\unitがUnicodeとして解釈できず
「Unicodeエスケープが不正です」
というエラーになってしまい
ます。愚かなOSと貧弱なコンパイラの悲しい衝突です。基本的にはJavaのコード 上ではフォルダ区切りに逆スラッシュは使わない方がよいでしょう。
逆スラッシュには円マークとの衝突という かつての通産省の小役人のくだらない小作業による混乱も入り、悲しさが 一層高まります。
なお、Unicodeエスケープ解釈は構文解析の前なので、例えば 'S'を'\u0053'で、'a'を'\u0061'で、'.'を'\u002e'で、'"'を'\u0022'で現した
public class Xtest {
public st\u0061tic void main(String[] args_){
\u0053ystem\u002eout\u002eprintf(\u0022Hello World%n");
}
}
のような記述もコンパイル、実行可能です。
2. Java構文解析時処理: \n
良く使う改行コード
\nは構文解析時に処理されます。次のプログラム
System.out.printf("Hello World\nAB");
// 出力
Hello World
AB
では構文解析/コンパイル時に"\n"が改行に置き換えられ、実行時はすでに
改行コードに置き換わった文字列が関数に引き渡されます。従って、文字エスケープ解釈を行わない関数であっても改行が出力 されます。
System.out.print("Hello World\nAB");
// 出力
Hello World
AB
\tや\bも構文解析時に変換されます。
注意しなくてはならないのが、これはコンパイル時に文字列リテラル に関して変換が行われるのであって、例えば"\n"(逆スラッシュの後にn) という文字列を持つテキストファイルを読み込んでそのまま出力 しても改行にはならないことです。
3. 関数内処理: %n (\\n,\\u000a)
例えばフォーマット付き印刷で使う改行指定
%nは、'%'という文字と'n'という文字の並びが関数に引き渡され 関数内でそれを改行を意味するものとして処理します。
System.out.printf("Hello World%nAB");
// 出力
Hello World
AB
あくまで関数が'%'と'n'を認識しているだけですので、その
認識をしない関数に引き渡すと改行としての機能は果たせません。
System.out.print("Hello World%nAB");
// 出力
Hello World%nAB
テキストファイルから"%n"を含む文字列を読み込んでprintfで出力す ると改行に変更されてしまうことに注意する必要があります。
"%n"を使用する場合留意しておかなければならないのが、出力される
コードが0x0Aになるとは限らないことです。コードはシステムによっては
0x0D0Aとなったり0x0Dとなることもあります。
例えばHTTP送信などでは"%n"は用いず"\r\n"を用いなくて
はなりません。
なお動作中のシステムでの改行コードは
System.getProperty("line.separator")
で取得できます。
Javaの正規表現クラス、Patternの正規表現設定関数は関数内で\nや
Unicodeエスケープを解釈できます。
その為、コンパイル時に展開されたエスケープでも
単純なエスケープ文字列でも同じように
動きます。
String p1="\u0053+\n\n\u0061*"; // コンパイル時解釈
String p2="\\u0053+\\n\\u000a\\u0061*"; // 実行時解釈
if( Pattern.matches(p1,"SSSS\n\naaa") ) {
System.out.println(p1+":MATCH");
}
if( Pattern.matches(p2,"SSSS\n\naaa") ){
System.out.println(p2+":MATCH");
}
// 実行結果
S+
a*:MATCH
\u0053+\n\u000a\u0061*:MATCH
実行結果を見ればp1の方はコンパイル時に解決されており、p2の方は
エスケープ形式になっていることが分かります。そして、どちらも同じ正規表現と解釈されています。
なお、p1の'\n'を'\u000a'と書くとコンパイルエラーになることは Java構文解析前のプリプロセスで説明した通りです。
| 固定リンク

