逆に書かれたこのコードが「Hello World!」を出力する理由


261

インターネットで見つけたコードは次のとおりです。

class M‮{public static void main(String[]a‭){System.out.print(new char[]
{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}    

このコードHello World!は画面に表示されます。ここで実行さていることがわかります。はっきりpublic static void main書いてあるように見えますが、逆です。このコードはどのように機能しますか?これはどのようにコンパイルされますか?

編集: IntellIJでこのコードを試しましたが、正常に動作します。ただし、何らかの理由で、cmdと一緒にnotepad ++では機能しません。私はまだその解決策を見つけていませんので、誰かが見つけた場合は、以下にコメントしてください。


38
これは面白いです... RTLサポートとは何か関係がありますか?
Eugene Sh。

12
Unicode文字#8237があります。直後Mと後[]afileformat.info/info/unicode/char/202d/index.htmこれは、左から右へのオーバーライドと呼ばれています
リバーサイド

45
必須のxkcd:xkcd.com/1137
Pac0

4
マウスを使用してコードスニペットで選択するだけで、ここで何が行われているのかを非常に簡単に確認できます。
Andreas Rejbrand

14
niam diov citats cilbupラテンのことわざのように聞こえる..
ミック・ニーモニック2017年

回答:


250

ここには、コードの表示方法を変更する非表示の文字があります。Intellijでは、コードを空の文字列("")にコピーアンドペーストして、Unicodeエスケープに置き換え、その影響を取り除き、コンパイラーが表示する順序を明らかにすることで、これらを見つけることができます。

コピーと貼り付けの出力は次のとおりです。

"class M\u202E{public static void main(String[]a\u202D){System.out.print(new char[]\n"+
        "{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}   "

ソースコード文字はこの順序で格納され、コンパイラはそれらをこの順序であるものとして処理しますが、表示は異なります。

\u202E右から左へのオーバーライドである文字に注意してください。すべての文字が右から左に表示されるように強制されるブロックを開始し、\u202Dネストされたブロックを開始する、左から右へのオーバーライドであるに文字は左から右の順序に強制され、最初のオーバーライドをオーバーライドします。

Ergoは、元のコードを表示するときclass Mは通常どおりに表示されますが、\u202Eそこからすべての表示順序\u202Dを逆にして、再びすべてを逆にします。(正式には、から\u202Dラインターミネーターまでのすべてが2回反転します。1回はのため、もう1回はのために\u202Dテキストの残りが反転します。\u202Eこのため、このテキストは行の最後ではなく中央に表示されます。)次のラインの方向性は、ラインターミネータにより、最初のラインの方向性とは無関係に処理{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}されるため、通常どおり表示されます。

完全な(非常に複雑な、数十ページにわたる)Unicode双方向アルゴリズムについては、Unicode Standard Annex#9を参照してください。


(表示ルーチンではなく)コンパイラーがこれらのUnicode文字自体をどう処理するかについては説明しません。私はそれらを完全に無視する(または空白として扱う)か、実際にソースコードに寄与していると解釈する可能性があります。ここではJavaのルールを知りませんが、未使用の識別子の最後に配置されているという事実から、後者の可能性があり、Unicode文字は実際にはそれらの識別子名の一部であることがわかります。
マルクファンレーウェン2017年

これはc#でも同じように機能しますか?
IanF1

14
@ IanF1これは、コンパイラー/インタープリターがRTLおよびLTR文字を空白としてカウントするすべての言語で機能します。しかし、これを行うことはありませんあなたはすべての値で、次の人の正気があなたによくなる可能性があなたのコードを、触れた場合、生産コードで。
wizzwizz4

2
または、言い換えると、「あなたのコードを維持することになる人が、あなたの住んでいる場所を知っている暴力的な精神病者であるかのように常にコーディングしてください。」、@ IanF1。または、「常に、コードを保守することになる人がStack Overflowの元の作者としてあなたに名前を付け、恥をかくようにコーディングする」
コーディグレイ

43

Unicode Bidirectional Algorithmのため、見た目は異なります。RLOとLROの2つの非表示文字があり、Unicode双方向アルゴリズムがこれら2つのメタ文字の間にネストされた文字の視覚的外観を変更するために使用します。

その結果、視覚的には逆の順序で表示されます、メモリ内の実際の文字は逆になりません。ここで結果を分析できます。JavaコンパイラーはRLOとLROを無視し、それらを空白として処理するため、コードがコンパイルされます。

注1:このアルゴリズムは、テキストエディターとブラウザーで、LTR文字(英語)とRTL文字(アラビア語、ヘブライ語など)の両方を同時に視覚的に表示するために使用されます。つまり、「双方向」です。双方向アルゴリズムの詳細については、UnicodeのWebサイトを参照してください
注2:LROおよびRLOの正確な動作は、アルゴリズムのセクション2.2で定義されています。


そのような機能の目的は何ですか?
Eugene Sh。

6
これらの文字は、アラビア語とヘブライ語を視覚的に正しく表示するために必要な場合があります。これらの言語は、右から左(RTL)で読み書きされます。読み書きされる最初の文字が右側に表示されます。詳しくはこちらをご覧ください
ジェームズローソン

アラビア語とヘブライ語の文字は本質的にRTLですが、明示的なオーバーライドがなくてもRTLとして表示され、近くにある他の特定の文字の順序も自動的に逆転します。ほとんどの場合句読点と考えられるため、明示的なオーバーライドが必要になることはほとんどありません。
user2357112は、

このページは、オーバーライドが必要な場合について説明します。@ user2357112は正しいですが、ほとんど必要ありません。実際、句読点、引用符、数字がある場合、これらの特殊文字は「中立」と見なされます。単語を読み取ってコンテキストを理解できないコンピューターの場合、それらをLTRとRTLのどちらとして扱うかは不明ですが、Bidiアルゴリズムはいくつかの順序を選択する必要があります。時々、それは「間違っている」ため、これらのオーバーライド文字を使用して「修正」する必要があります。
ジェームズローソン

3
また、U + 202EおよびU + 202Dは空白とは見なされません。Javaは、ASCIIスペース、水平タブ、フォームフィード、およびCR / LF / CRLFのみを空白と見なします。これらは実際には辞書のM\u202Eおよびの一部ですa\u202Dが、これらの識別子はMおよびと同等に扱われるようaです。(JLSはこれをうまく説明していません。)
user2357112は、Monica

28

キャラクターU+202Eはコードを右から左にミラーリングしますが、とても賢いです。Mから隠されて、

"class M\u202E{..."

この背後にある魔法をどのように見つけましたか?

さて、最初「私は冗談のようで、他の誰かを失うのは難しい」という質問を見たとき、IDE( "IntelliJ")を開いてクラスを作成し、コードを通過しました... そしてそれはコンパイルされました!そこで、よく見てみると、 "public static void"が後方にあることがわかりました。そこで、カーソルを置いてそこに移動し、いくつかの文字を消去しました ...そして何が起こりますか?文字は、後方消去始め、私は私がプログラムを実行するために進んだからうーん....珍しい...私は...それを実行しなければならないと思ったので、しかし、最初に私は必要それを保存するために ...としたそのとき、Iそれを見つけた!。私のIDEが一部の文字に異なるエンコーディングがあると言ったのでファイルを保存できませんでした。、それで私は仕事をすることができる特別なcharsのためにGoogleで研究を始めます、そしてそれだけです:)

少しについて

Unicode双方向アルゴリズム、およびU+202E関連する簡単な説明

Unicode標準では、論理順序と呼ばれるメモリ表現順序が規定されています。テキストが横線で表示される場合、ほとんどのスクリプトは文字を左から右に表示します。ただし、いくつかのスクリプト(アラビア語やヘブライ語など)には、表示される水平方向のテキストの自然な順序が右から左にあるものがあります。すべてのテキストの水平方向が均一である場合、表示テキストの順序は明確です。

ただし、これらの右から左へのスクリプトは左から右に書かれる数字を使用するため、テキストは実際には双方向です。つまり、右から左へのテキストと左から右へのテキストの混合です。数字に加えて、英語やその他のスクリプトの埋め込み単語も左から右に書かれ、双方向のテキストも生成します。明確な仕様がないと、テキストの水平方向が均一でない場合、表示される文字の順序を決定するときに曖昧さが生じる可能性があります。

この付録では、双方向Unicodeテキストの方向性を決定するために使用されるアルゴリズムについて説明します。このアルゴリズムは、現在多くの既存の実装で採用されている暗黙的なモデルを拡張し、特別な状況のために明示的なフォーマット文字を追加します。ほとんどの場合、正しい表示順序を得るために、テキストに追加情報を含める必要はありません。

ただし、双方向テキストの場合、理解可能なテキストを生成するために暗黙的な双方向順序付けでは不十分な状況があります。これらのケースに対処するために、レンダリング時の文字の順序を制御するために、方向設定文字の最小限のセットが定義されています。これにより、読みやすいインターチェンジの表示順序を正確に制御でき、ファイル名やラベルなどの単純なアイテムに使用されるプレーンテキストを常に正しく表示できるようにできます。

なぜこのようなアルゴリズムを作成するのですか?

Bidiアルゴリズムは、アラビア語またはヘブライ語の文字のシーケンスを右から左に順番にレンダリングできます。


4

言語仕様の第3章では、Javaプログラムの字句変換がどのように行われるかを詳細に説明することによって説明します。質問にとって最も重要なこと:

プログラムはUnicode(§3.1)記述されていますが、字句変換が提供され(§3.2)、Unicodeエスケープ(§3.3)を使用して、ASCII文字のみを使用する任意のUnicode文字を含めることができます。

したがって、プログラムはUnicode文字で記述されており\uxxxx、ファイルのエンコードがUnicode文字をサポートしていない場合、作成者はそれらをエスケープして、適切な文字に変換できます。この場合に存在するUnicode文字の1つは\u202Eです。スニペットでは視覚的には表示されませんが、ブラウザーのエンコードを切り替えようとすると、非表示の文字が表示される場合があります。

したがって、字句変換はクラス宣言になります。

class M\u202E{

つまり、クラス識別子はM\u202Eです。仕様は、有効な識別子ですとしてこれを考慮します。

Identifier:
    IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
IdentifierChars:
    JavaLetter {JavaLetterOrDigit}

「Javaの文字または数字」は、メソッドCharacter.isJavaIdentifierPart(int)がtrueを返す文字です。


申し訳ありませんが、これは後方です(しゃれが意図されています)。ソースコードにはエスケープはありません。あなたはそれがどのように書かれたかを説明しています。そして、「M」という名前のクラス(1文字だけ)にコンパイルされます。
トムブロジェット2017年

@TomBlodget確かにポイント(実際、仕様の引用で強調表示されています)は、コンパイラーが未加工のUnicode文字も処理できることです。それは本当に全体の説明です。エスケープ翻訳は単なる追加情報であり、このケースとは直接関係ありません。コンパイルされたクラスに関しては、RTLスイッチ文字がコンパイラによって何らかの理由で破棄されているためだと思います。これが予想されるかどうかを確認しようと思いますが、字句変換フェーズの後で起こると思います。
M Anouti 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.