web-dev-qa-db-ja.com

特定のUnicode文字を含むコメント内でJavaコードを実行することが許可されるのはなぜですか?

次のコードは、「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仕様で許可されているのはなぜですか?

1302
Reg

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

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

[...] Javaプログラミング言語は、Unicodeで記述されたプログラムをASCIIに変換する標準的な方法を指定します。これにより、プログラムは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:コメント内のコードの実行?!

724
aioobe

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

その背後にある考え方は、異なる文字エンコーディング間でのJavaソースコードの無損失変換を可能にするということでした。今日、広範なUnicodeサポートがあり、これは問題には思えませんが、当時、西側の開発者がアジアの同僚からアジアの文字を含むソースコードを受け取って変更することは容易ではありませんでした。それをコンパイルしてテストすること、そして結果を送り返すことを含みます。

そのため、Javaのソースコードはどのようなエンコーディングでも書くことができ、識別子、characterとStringのリテラルとコメントの中に広範囲の文字を入れることができます。次に、それを無損失で転送するために、ターゲットのエンコーディングでサポートされていないすべての文字がUnicodeエスケープで置き換えられます。

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

これは言及されていないもう一つの奇妙な機能の理由です:\uuuuuuxxxx構文:

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

136
Holger

私は自分自身を助けることができず、まだそれがなされているのを見たことがないという理由で、間違っているという隠された前提が含まれているためコメント!

Javaソースコードでは、\ u000dはあらゆる点でASCIICR文字と同等です。それがどこで発生しても、それは普通で単純な行末です。問題のフォーマットは誤解を招くようなものです。その文字シーケンスが実際に構文的に対応しているものは次のとおりです。

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

私見の最も正しい答えはそれゆえです:それはコメントの中にないのでコードが実行されます。それは次の行にあります。あなたが期待するのと同じように、 "コメントでのコードの実行"はJavaでは許可されていません。

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

100
Pepijn Schmitz

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

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

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

Cファミリにも同様の設計エラーがありますが、1 コメント境界が決定される前にバックスラッシュと改行が処理されます。

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

これを取り上げて、この特定の設計エラーを簡単に犯すことができることを説明し、コンパイラープログラマーが考える方法をトークン化と構文解析について熟知している場合、修正するには手遅れになるまでエラーであることに気付かないことを説明しますトークン化と解析について。基本的に、正式な文法をすでに定義していて、誰かが構文上の特殊なケースを考え出した場合-トライグラフ、バックスラッシュ-改行、ASCIIに制限されたソースファイル内の任意のUnicode文字をエンコードする変換パスを追加しますbeforeトークナイザーを再定義して、その特殊なケースを使用する意味がある場所に注意を向けます。

1 退屈な人へ:Cのこの側面は100%意図的であり、これを構成するわけではないという理由で、パンチされたカードに任意の長い行のコードを機械的に強制適合できることを知っています。まだ間違った設計決定でした。

65
zwol

これは意図的な設計上の選択であり、Javaの元の設計にまでさかのぼります。

「コメントでUnicodeエスケープをしたいのですが」と尋ねる人々には、私は彼らの母国語がラテン文字セットを使っている人々であると思います。言い換えれば、Javaプログラムの正当なところであればどこでも、最も一般的にはコメントや文字列で、人々が任意のUnicode文字を使用できることは、Javaの元の設計に固有のものです。

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

21

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

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

コメントに\uxxxxを付ける意味は全くありません - 誰もそれを読むことができません。

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

デザイナーは1995年に彼らの理由を持っていました、しかし20年後、これは間違った選択であるように見えます。

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

20
ZhongYu

なぜUnicodeエスケープが実装されているのと同じ理由で答えられるのは、仕様を書いた人々だけです。

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

  • 任意のBMP文字を使用できるようにしたいです。
  • あなたは、任意のBMP文字をかなり簡単に入力できるようにしたいのです。これを行う方法は、Unicodeエスケープを使用することです。
  • あなたは、人間が読み書きしやすいように語彙の仕様を簡単に、そして同様に合理的に実装しやすいようにしたいと思います。

Unicodeのエスケープが紛れ込んでしまうと、これは非常に困難になります。新しいレクサー規則が大量に作成されます。

簡単な方法は、最初にすべてのUnicodeエスケープを検索してそれが表す文字で置き換え、次に結果の文書をUnicodeエスケープが存在しないかのように解析することです。

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

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

11
Martijn