特定のUnicode文字を含むコメントでJavaコードを実行できるのはなぜですか?


1356

次のコードは、「Hello World!」という出力を生成します。(いいえ、実際に試してみてください)。

public static void main(String... args) {

   // The comment below is not a typo.
   // \u000d System.out.println("Hello World!");
}

これは、JavaコンパイラがUnicode文字\u000dを新しい行として解析し、次のように変換されるためです。

public static void main(String... args) {

   // The comment below is not a typo.
   //
   System.out.println("Hello World!");
}

したがって、「実行」されるコメントになります。

これは悪意のあるコードや悪意のあるプログラマが思いつくものを「隠す」ために使用できるので、なぜコメントで許可されているのですか?

なぜこれがJava仕様で許可されているのですか?


44
「なぜこれが許されるのか」は私にはあまりにも意見に基づいているようです。言語デザイナーが決定を下しましたが、他に何を知る必要がありますか?その決定を下す人の発言を見つけない限り、私たちは推測することしかできません。
IngoBürk15年

194
興味深い点の1つは、少なくともOPのIDEが明らかに誤解し、誤った強調表示を表示していることです
dhke


47
@Tobbしかし、JavaデザイナーがSOにアクセスしているので、そのうちの1人が回答を得ることができます。また、すでにこの質問に回答しているリソースが存在する場合もあります。
Pshemo 2015年

41
簡単な答えは、言語の規則により、コードがコメントに含まれていないため、質問の形式が正しくないということです。
ローンの侯爵2015年

回答:


741

Unicodeのデコードは、他の字句変換の前に行われます。これの主な利点は、ASCIIと他のエンコーディングの間を行き来するのが簡単になることです。コメントの始まりと終わりを把握する必要さえありません!

JLSセクション3.3で述べたように、これにより、ASCIIベースのツールでソースファイルを処理できます。

[...] Javaプログラミング言語は、プログラムをASCIIベースのツールで処理できる形式に変更する、Unicodeで記述されたプログラムをASCIIに変換する標準的な方法を指定します。[...]

これにより、常にJavaプラットフォームの主要な目標であったプラットフォームの独立性(サポートされる文字セットの独立性)が根本的に保証されます。

ファイル内の任意の場所に任意のUnicode文字を書き込むことができることは、きちんとした機能であり、非ラテン語でコードを文書化するときのコメントでは特に重要です。このような微妙な方法でセマンティクスに干渉する可能性があるという事実は、(不幸な)副作用にすぎません。

このテーマには多くの落とし穴があり、Joshua BlochとNeal GafterによるJava Puzzlersには次のバリアントが含まれていました。

これは合法的なJavaプログラムですか?もしそうなら、それは何を印刷しますか?

\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020
\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079
\u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020
\u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063
\u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028
\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020
\u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020
\u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b
\u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d

(このプログラムは単純な「Hello World」プログラムであることが判明しています。)

謎を解くための解決策として、彼らは次のことを指摘しています。

さらに真剣に、このパズルは前の3つのレッスンを強化するのに役立ちます。Unicodeエスケープは、プログラムに他の方法では表現できない文字を挿入する必要がある場合に不可欠です。他のすべてのケースではそれらを避けてください。


出典:Java:コメントでコードを実行していますか?!


84
要するに、Javaは意図的にそれを許可しています。「バグ」はOPのIDEにありますか?
バトシェバ2015年

60
@Bathsheba:それは人々の頭の中にもっとあります。人々はJava構文解析がどのように機能するかを理解しようとしないので、IDEはコードを間違った方法で表示することがあります。上の例では、コメント\u000dはコードの終わりで終わり、その後ろの部分がコードで強調表示されます。
アーロンディグラ2015年

62
もう1つの一般的な間違いは、Windowsパスをコードに貼り付けることです。// C:\user\...これ\userは、有効なUnicodeエスケープシーケンスではないため、コンパイルエラーを引き起こします。
アーロンディグラ2015年

50
日食では、後のコード\u000dが部分的に強調表示されます。Ctrl + Shift + Fを押すと、文字が新しい行に置き換えられ、残りの行が折り返されます
bluelDe

20
@TheLostMind私が答えを正しく理解していれば、ブロックコメントでもこれを再現できるはずです。 \u002A/コメントを終了する必要があります。
Taemyr

141

これはまだ対処されていないので、ここでの説明では、Unicodeエスケープの変換が他のソースコード処理の前に行われる理由を説明します。

その背後にあるアイデアは、異なる文字エンコーディング間でJavaソースコードのロスレス変換を可能にするというものでした。今日、Unicodeが広くサポートされており、これは問題のようには見えませんが、当時、西側諸国の開発者がアジアの文字を含むアジアの同僚からソースコードを受け取って変更を加えることは容易ではありませんでした(コンパイルとテストを含めて)、結果を返送します。

そのため、Javaソースコードは任意のエンコーディングで記​​述でき、識別子、文字、Stringリテラル、コメント内でさまざまな文字を使用できます。次に、それをロスレスで転送するために、ターゲットエンコーディングでサポートされていないすべての文字がUnicodeエスケープで置き換えられます。

これは可逆的なプロセスであり、興味深いのは、変換ルールはJavaソースコードの構文に依存しないため、Javaソースコードの構文について何も知る必要がないツールで変換できることです。これは、コンパイラ内の実際のUnicode文字への変換がJavaソースコード構文とは独立して行われるため、機能します。これは、ソースコードの意味を変更せずに、双方向で任意の数の変換ステップを実行できることを意味します。

これが、言及されていない別の奇妙な機能の理由です:\uuuuuuxxxx構文:

翻訳ツールが文字をエスケープしていて、すでにエスケープuされたシーケンスであるシーケンスを検出した場合、シーケンスに追加を挿入してに変換する\ucafe必要があり\uucafeます。意味は変わりませんが、逆方向に変換する場合、ツールは1つuを削除し、1つを含むシーケンスのみをuUnicode文字で置き換える必要があります。このように、Unicodeエスケープでも、前後に変換するときに元の形式で保持されます。たぶん、誰もその機能を使ったことはない…


1
興味深いことに、構文native2asciiを使用していないようです\uu...xxxx
ninjalj

5
ええ、latin-1のみを読み取るnative2asciiようProperties.loadに修正されているため、リソースバンドルをiso-latin-1に変換することでリソースバンドルの準備を支援することを目的としていました。そして、そこにはルールが異なり、\uuu…構文も初期の処理段階もありません。プロパティファイルでproperty=multi\u000alineは、は実際にと同じproperty=multi\nlineです。(ドキュメントの「Java™言語仕様のセクション3.3で定義されているUnicodeエスケープを使用する」という句に矛盾します)
Holger

10
この設計目標は、いぼがなくても達成できたことに注意してください。最も簡単な方法は、\uエスケープを禁止してU + 0000–007Fの範囲の文字を生成することでした。(そのような文字はすべて、1990年代に関連していたすべての国別エンコーディングでネイティブに表現できます。まあ、おそらく一部の制御文字は除きますが、とにかくJavaを書くのにそれらは必要ありません。)
zwol

3
@zwol:まあ、とにかくJavaソースコード内で許可されていない制御文字を除外するなら、あなたは正しいです。それにもかかわらず、それはルールをより複雑にすることを意味します。そして今日、決定を議論するには遅すぎます…
Holger

ラテン語などではなく、utf8でドキュメントを保存する問題。この西洋のナンセンスのため、私のデータベースもすべて壊れていました
David天宇Wong

106

私は自分自身を助けることができず、それがまだなされていないので、間違った隠された前提が含まれているため、質問が無効である、つまりコードがコメント!

Javaソースコードでは、\ u000dはあらゆる点でASCII CR文字と同等です。どこにいても、シンプルでシンプルなラインエンディングです。問題のフォーマットは誤解を招くものであり、その文字のシーケンスが実際に構文的に対応するものは次のとおりです。

public static void main(String... args) {
   // The comment below is no typo. 
   // 
 System.out.println("Hello World!");
}

したがって、私にとって最も正解は次のとおりです。コードがコメントにないため、コードが実行されます。次の行にあります。「コメント内のコードの実行」は、予想どおりJavaでは許可されていません。

混乱の多くは、構文ハイライターとIDEがこの状況を考慮に入れるほど洗練されていないという事実から生じます。ユニコードエスケープをまったく処理しないか、以前のようにでjavacはなく、コードを解析した後で処理します。


6
これはJavaの「設計エラー」ではないことに同意しますが、これはIDEのバグです。
bvdb 2017年

3
問題はむしろ、言語のこの特定の側面に精通していない人にコメントのように見え、おそらく構文の強調表示を参照しないコードが実際にコメントではない理由についてです。質問が無効であるという前提に基づく反対は不誠実です。
Phil

@Phil:特定のツールで表示するとコメントのように見えるだけですが、それ以外の場合は表示されます。
jmoreno

1
@jmoreno コードを読み取るために、テキストエディター以上のものを用意する必要はありません。少なくとも、これは最小の驚きの原則に違反します。つまり、//スタイルのコメントは次の\ n文字まで続きます-最終的に\ nによって最終的に置き換えられる他のシーケンスには続きません。コメントが取り除かれる以外のものであることは決して期待されません。プリプロセッサが不正です。
Phil

69

\u000dので、エスケープがコメントを終了\uエスケープは一様に対応するUnicode文字に変換されます前に、プログラムがトークン化されます。\u0057\u0057代わりに//を使用してコメントを開始することもできます。

これはIDEのバグであり、行の構文を強調表示して\u000d、コメントの終わりを明確にする必要があります。

これは、言語の設計エラーでもあります。これは修正できません。依存しているプログラムが壊れてしまうからです。 \uエスケープは、「意味のある」コンテキスト(文字列リテラルと識別子、おそらく他の場所ではない)でのみ、コンパイラーによって対応するUnicode文字に変換するか、U + 0000–007Fの範囲の文字を生成することを禁止する必要があります。 、 または両方。これらのセマンティクスのいずれかにより、\u000dエスケープ\uが役立つ場合を妨げることなく、コメントがエスケープによって終了するのを防ぐことができます。これは、ラテン語以外のスクリプトでコメントをエンコードする方法としてコメント内でのエスケープの使用を含むことに注意してください。\uテキストエディターは、\uエスケープはコンパイラよりも重要です。(ただし、\uエスケープを任意のコンテキストで対応する文字として表示するエディタまたはIDEについては知りません。)

Cの家族の中で同様の設計誤りがあり、1バックスラッシュ-改行コメントの境界前に処理されるなどして、決定されています

// this is a comment \
   this is still in the comment!

これを取り上げて、この特定のデザインエラーを簡単に作成できることを説明します。トークン化やコンパイラプログラマーの考え方の分析に慣れている場合は、修正するのが遅すぎるまではエラーであることに気づきません。トークン化と解析について。基本的に、すでに正式な文法を定義していて、誰かが構文の特別なケースを思いついた場合-トリグラフ、バックスラッシュ-改行、ASCIIに制限されたソースファイル内の任意のUnicode文字のエンコードなど、何でも差し込む必要がある場合は、トークナイザーを再定義するよりも、トークナイザーの前に変換パスを追加して、その特殊なケースを使用する意味がある場所に注意を払います。

1ペダルについて:私は、Cのこの側面が100%意図的であったことを承知しています。理論的根拠はありません—私はこれを作り上げていません—パンチされたカードに任意の長い行でコードを機械的に強制的に適合させることができます。それはまだ間違った設計決定でした。


17
これは設計エラーだとは言えません。これは設計の選択としては不適切であるか、不幸な結果をもたらす選択であることに同意することはできますが、言語の設計者が意図したとおりに機能すると思います。これにより、ASCIIエンコーディングを維持しながら、ファイル内の任意の場所で任意のUnicode文字を使用できますファイルの。
aioobe 2015年

12
そうは言って\uも、8進表記に先行ゼロを使用することでCの先導に従うという決定よりも、処理ステージの選択は不合理ではなかったと思います。8進表記が役立つこともありますが、先行ゼロがそれを示すのに良い方法である理由について、誰もが明確に述べていることはまだ聞いていません。
スーパーキャット2015年

3
@supercatその機能をC89に投入した人々は、最初から機能を設計するのではなく、元のK&Rプリプロセッサの動作を一般化していました。私は、彼らはパンチカードのベストプラクティスに精通した疑い、私も疑問機能がいることを、これまでの述べられた目的のために使用されて、1つのまたは2 retrocomputing演習のために多分除きます。
zwol

8
@supercat \uU + 0000..U + 007Fの範囲の文字を生成することが禁止されていれば、トークン化前の変換としてJavaに問題はありません。これは、「どこでも機能する」と「構文上の重要性を持つASCII文字のエイリアス」の組み合わせであり、厄介なものからフラットなものへと下がっています。
zwol

4
「ペダント向け」について:もちろん、当時//1行のコメントはありませんでした。また、Cには改行ではないステートメントターミネータがあるため、K&Rから「文字列リテラル連結」存在することを確認できる限り、Cは長い文字列に使用されます。
Mark Hurd

22

これは、Javaの元の設計にまで遡る意図的な設計の選択でした。

「コメントにUnicodeエスケープが必要なのは誰ですか?」と尋ねる人々には、ネイティブ言語がラテン文字セットを使用している人々だと思います。言い換えると、Javaプログラムの合法な場所であればどこでも、最も一般的にはコメントや文字列で、任意のUnicode文字を使用できることは、Javaの元の設計に固有のものです。

これは間違いなく、ソーステキストを表示するために使用されるプログラム(IDEなど)の欠点であり、そのようなプログラムはUnicodeエスケープを解釈して対応するグリフを表示することができません。


8
現在、ソースコードにUTF-8を使用しており、エスケープを必要とせずにUnicode文字を直接使用できます。
–PaŭloEbermann 2015

21

これは設計ミスであると@zwolに同意します。しかし、私はそれに対してさらに批判的です。

\uエスケープは文字列および文字リテラルで役立ちます。それが存在するべき唯一の場所です。これは、他のエスケープと同じように処理する必要があり\nます。そして、正確に意味する"\u000A" 必要があります"\n"

\uxxxxコメントを書いてもまったく意味がありません。誰もそれを読むことはできません。

同様\uxxxxに、プログラムの他の部分で使用する意味はありません。唯一の例外はおそらく、いくつかの非ASCII文字を含むように強制されているパブリックAPIです-それを最後に見たのはいつですか?

デザイナーには1995年に理由がありましたが、20年後、これは間違った選択のようです。

(読者への質問-なぜこの質問は新しい票を獲得し続けるのですか?この質問は人気のある場所からリンクされていますか?)


5
おそらく、ASCII以外の文字がAPI​​で使用されているのではありません。アジア諸国などで、私ではなくそれを使用している人がいます。また、識別子に非ASCII文字を使用している場合、ドキュメントのコメントでそれらを禁止することはほとんど意味がありません。それにもかかわらず、トークン内でそれらを許可することと、トークンの意味または境界を変更することを許可することは、異なるものです。
Holger

15
適切なファイルエンコーディングを使用できます。int \u5431できるときに書く理由int 整
ZhongYu

3
ときあなたは何をするか、あなたが彼らのAPIに対してコードをコンパイルする必要があり、適切なエンコーディングを使用することはできません(広範囲に存在しなかったことを前提とUTF-8サポートは1995年)。1つのメソッドを呼び出すだけで、その単一のメソッドにオペレーティングシステムのアジア言語サポートパック(90年代を思い出してください)をインストールしたくない...
Holger

5
1995年よりもずっと明確になっているのは、プログラミングをしたいなら、英語をよく知っているということです。プログラミングは国際的な交流であり、ほとんどすべてのリソースは英語です。
ZhongYu

8
これは変わったとは思いません。ほとんどの場合、Javaのドキュメントもすべて英語でした。しばらくの間、日本語の翻訳が維持されていましたが、2つの言語を維持しても、世界のすべてのロケールでそれを維持するという考えは裏付けられません(むしろそれを反証しました)。それまでは、識別子にUnicodeをサポートする主流の言語はありませんでした。だから誰かがローカライズされたソースコードが次の大きなものだと思ったのではないでしょうか。私が言うありがたいそれは離陸しませんでした。
Holger

11

Unicodeエスケープがそのまま実装された理由に答えられるのは、仕様を作成した人だけです。

これのもっともらしい理由は、Javaソースコードの可能な文字としてBMP全体を許可したいという要望があったためです。ただし、これには問題があります。

  • 任意のBMPキャラクターを使用できるようにしたい。
  • BMP文字をかなり簡単に入力できるようにしたい。これを行う方法は、Unicodeエスケープを使用することです。
  • 語彙の仕様を人間が読み書きしやすくし、実装もかなり簡単にする必要があります。

これは、Unicodeエスケープが複雑になると信じられないほど困難です。新しいレクサールールが大量に作成されます。

簡単な方法は、2つのステップで字句解析を行うことです。最初にすべてのUnicodeエスケープを検索し、それを表す文字で置き換えます。次に、Unicodeエスケープが存在しないかのように結果のドキュメントを解析します。

これの利点は、指定が簡単であるため、仕様が単純になり、実装が容易になることです。

欠点は、まあ、あなたの例です。


2
または、\ uxxxxの使用を識別子、文字列リテラル、および文字定数に制限します。これがC11の機能です。
ninjalj

ただし、パーサールールは本当に複雑になります。なぜなら、これらはそれらを定義するものだからです。
Martijn、2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.