2011-01-29 7 views
44

ミステリー

Java識別子でどの文字が許可されているかを正確に調べてみると、非常に不思議な点がありました。Javaが識別子の制御文字を許可するのはなぜですか?

私は、Java識別子は、先頭のアンダースコアのためにとドル記号のために付与された例外を除いて、彼らはUnicodeプロパティID_Startを持っているし、プロパティID_Continueを持つものが続いている文字で始まる要件に適合していることを見つけることが期待だろう。それは事実ではないとわかりました。私が見つけたのは、これまでに聞いたことのある通常の識別子のアイデアや他のアイデアとの極端な違いです。

ショートデモ

は、次のデモは、ASCII ESC文字(8進数033)はJava識別子では許可されていることを証明考えてみましょう:

$ perl -le 'print qq(public class escape { public static void main(String argv[]) { String var_\033 = "i am escape: \033"; System.out.println(var_\033); }})' > escape.java 
$ javac escape.java 
$ java escape | cat -v 
i am escape: ^[ 

それはしかし、それよりもさらに悪いです。事実、無限に悪化しています。 NULLでも可能です!また、識別子文字でもない他の何千ものコードポイント。私はこれをSolaris、Linux、Darwinを実行しているMacでテストしましたが、すべて同じ結果が得られます。ここで

ロングデモ

は、Javaはかなりoutrageuosly法的な識別子名の一部として可能にすべてのこれらの予想外のコード・ポイントが表示されますテストプログラムです。ここで

#!/usr/bin/env perl 
# 
# test-java-idchars - find which bogus code points Java allows in its identifiers 
# 
# usage: test-java-idchars [low high] 
# e.g.: test-java-idchars 0 255 
# 
# Without arguments, tests Unicode code points 
# from 0 .. 0x1000. You may go further with a 
# higher explicit argument. 
# 
# Produces a report at the end. 
# 
# You can ^C it prematurely to end the program then 
# and get a report of its progress up to that point. 
# 
# Tom Christiansen 
# [email protected] 
# Sat Jan 29 10:41:09 MST 2011 

use strict; 
use warnings; 

use encoding "Latin1"; 
use open IO => ":utf8"; 

use charnames(); 

$| = 1; 

my @legal; 

my ($start, $stop) = (0, 0x1000); 

if (@ARGV != 0) { 
    if (@ARGV == 1) { 
     for (($stop) = @ARGV) { 
      $_ = oct if /^0/; # support 0OCTAL, 0xHEX, 0bBINARY 
     } 
    } 
    elsif (@ARGV == 2) { 
     for (($start, $stop) = @ARGV) { 
      $_ = oct if /^0/; 
     } 
    } 
    else { 
     die "usage: $0 [ [start] stop ]\n"; 
    } 
} 

for my $cp ($start .. $stop) { 
    my $char = chr($cp); 

    next if $char =~ /[\s\w]/; 

    my $type = "?"; 
    for ($char) { 
     $type = "Letter"  if /\pL/; 
     $type = "Mark"  if /\pM/; 
     $type = "Number"  if /\pN/; 
     $type = "Punctuation" if /\pP/; 
     $type = "Symbol"  if /\pS/; 
     $type = "Separator" if /\pZ/; 
     $type = "Control"  if /\pC/; 
    } 
    my $name = $cp ? (charnames::viacode($cp) || "<missing>") : "NULL"; 
    next if $name eq "<missing>" && $cp > 0xFF; 
    my $msg = sprintf("U+%04X %s", $cp, $name); 
    print "testing \\p{$type} $msg..."; 
    open(TESTPROGRAM, ">:utf8", "testchar.java") || die $!; 

print TESTPROGRAM <<"End_of_Java_Program"; 

public class testchar { 
    public static void main(String argv[]) { 
     String var_$char = "variable name ends in $msg"; 
     System.out.println(var_$char); 
    } 
} 

End_of_Java_Program 

    close(TESTPROGRAM) || die $!; 

    system q{ 
     (javac -encoding UTF-8 testchar.java \ 
      && \ 
      java -Dfile.encoding=UTF-8 testchar | grep variable \ 
     ) >/dev/null 2>&1 
    }; 

    push @legal, sprintf("U+%04X", $cp) if $? == 0; 

    if ($? && $? < 128) { 
     print "<interrupted>\n"; 
     exit; # from a ^C 
    } 

    printf "is %s in Java identifiers.\n", 
     ($? == 0) ? uc "legal" : "forbidden"; 

} 

END { 
    print "Legal but evil code points: @legal\n"; 
} 

は空白でも識別文字でもないちょうど最初の33個のコード・ポイントにそのプログラムを実行しているのサンプルです:

$ perl test-java-idchars 0 0x20 
testing \p{Control} U+0000 NULL...is LEGAL in Java identifiers. 
testing \p{Control} U+0001 START OF HEADING...is LEGAL in Java identifiers. 
testing \p{Control} U+0002 START OF TEXT...is LEGAL in Java identifiers. 
testing \p{Control} U+0003 END OF TEXT...is LEGAL in Java identifiers. 
testing \p{Control} U+0004 END OF TRANSMISSION...is LEGAL in Java identifiers. 
testing \p{Control} U+0005 ENQUIRY...is LEGAL in Java identifiers. 
testing \p{Control} U+0006 ACKNOWLEDGE...is LEGAL in Java identifiers. 
testing \p{Control} U+0007 BELL...is LEGAL in Java identifiers. 
testing \p{Control} U+0008 BACKSPACE...is LEGAL in Java identifiers. 
testing \p{Control} U+000B LINE TABULATION...is forbidden in Java identifiers. 
testing \p{Control} U+000E SHIFT OUT...is LEGAL in Java identifiers. 
testing \p{Control} U+000F SHIFT IN...is LEGAL in Java identifiers. 
testing \p{Control} U+0010 DATA LINK ESCAPE...is LEGAL in Java identifiers. 
testing \p{Control} U+0011 DEVICE CONTROL ONE...is LEGAL in Java identifiers. 
testing \p{Control} U+0012 DEVICE CONTROL TWO...is LEGAL in Java identifiers. 
testing \p{Control} U+0013 DEVICE CONTROL THREE...is LEGAL in Java identifiers. 
testing \p{Control} U+0014 DEVICE CONTROL FOUR...is LEGAL in Java identifiers. 
testing \p{Control} U+0015 NEGATIVE ACKNOWLEDGE...is LEGAL in Java identifiers. 
testing \p{Control} U+0016 SYNCHRONOUS IDLE...is LEGAL in Java identifiers. 
testing \p{Control} U+0017 END OF TRANSMISSION BLOCK...is LEGAL in Java identifiers. 
testing \p{Control} U+0018 CANCEL...is LEGAL in Java identifiers. 
testing \p{Control} U+0019 END OF MEDIUM...is LEGAL in Java identifiers. 
testing \p{Control} U+001A SUBSTITUTE...is LEGAL in Java identifiers. 
testing \p{Control} U+001B ESCAPE...is LEGAL in Java identifiers. 
testing \p{Control} U+001C INFORMATION SEPARATOR FOUR...is forbidden in Java identifiers. 
testing \p{Control} U+001D INFORMATION SEPARATOR THREE...is forbidden in Java identifiers. 
testing \p{Control} U+001E INFORMATION SEPARATOR TWO...is forbidden in Java identifiers. 
testing \p{Control} U+001F INFORMATION SEPARATOR ONE...is forbidden in Java identifiers. 
Legal but evil code points: U+0000 U+0001 U+0002 U+0003 U+0004 U+0005 U+0006 U+0007 U+0008 U+000E U+000F U+0010 U+0011 U+0012 U+0013 U+0014 U+0015 U+0016 U+0017 U+0018 U+0019 U+001A U+001B 

そして、ここでは別のデモです:

$ perl test-java-idchars 0x600 0x700 | grep -i legal 
testing \p{Control} U+0600 ARABIC NUMBER SIGN...is LEGAL in Java identifiers. 
testing \p{Control} U+0601 ARABIC SIGN SANAH...is LEGAL in Java identifiers. 
testing \p{Control} U+0602 ARABIC FOOTNOTE MARKER...is LEGAL in Java identifiers. 
testing \p{Control} U+0603 ARABIC SIGN SAFHA...is LEGAL in Java identifiers. 
testing \p{Control} U+06DD ARABIC END OF AYAH...is LEGAL in Java identifiers. 
Legal but evil code points: U+0600 U+0601 U+0602 U+0603 U+06DD 

質問

誰もこの一見狂った動作を説明できますか?おそらく最も不思議なU + 0000で始まって、そこには数多くの多くの他にも多くの不可解なコードポイントがあります。最初の0x1000コードポイントで実行すると、プロパティーCurrent_Symbolで任意のコードポイントとすべてのコードポイントを許可するなど、特定のパターンが表示されます。しかし、あまりにも多くのことは、少なくとも私によっては説明できません。

+0

使用しているJava言語のバージョンは? IIRCの初期のバージョンでは、「文字がいくつかの特定のクラスに属さない場合は、英数字として扱われ、識別子で使用できます」という規則がありましたが、それは後のバージョンでは削除されたと考えられました。 – finnw

+2

これは本当に吸う、そしてそれは本当に良い質問です。私はなぜ彼らが識別子の国の文字を許可するのか理解できません。中国の識別子を使用した場合、ソースがどのように見えるか想像してみてください。または日本語。または右から左に書かれたヘブライ語でさえ。 C#の場合も同じです。私の同僚の一人は、実際にロシアの識別子を使用しています。それはひどく見え、しばしばネイティブのロシア語(自分自身を含む)のためにも。 –

+0

@finnw:これはJavaのバージョン1.6です。 – tchrist

答えて

13

Java Language Specification section 3.8は、Character.isJavaIdentifierStart()およびCharacter.isJavaIdentifierPart()に向かっている。後者は、他の条件の中でもCharacter.isIdentifierIgnorable()です。これは、空白以外の制御文字(C1の範囲全体を含む、リストのリンクを参照してください)を許可します。

+8

Javaでは、Unicode標準とは違う形で独自の定義を作成することに決めました。 Unicodeの 'Default_Ignorable_Code_Point'プロパティが不思議な目的のために不十分であることが証明された理由、なぜUnicodeと矛盾する独自の定義を発明しなければならなかったのかを本当に知りたいですか?これは、Unicodeとは異なる独自の概念を持つJavaと同じくらい意味があります。 – tchrist

+2

U + 0602アラビア語脚注マーカーやU + 070F SYRIAC略称マークやU + 2062不可能なTIMESはすべてGc = Cf aka General_Category = Formatのような不可解なコードポイントの多くを説明していません。それらは '\ w'文字を全くタイプしません!とにかく**目に見えない**は何ですか?何を喫煙していますか?あなたはどちらが重要であり、どちらが無視できるのかを知ることはできません。大文字と小文字を区別しないで、すべての母音が等しいかどうかを調べる識別子を使用することもできます。多くの(非)感覚を作ります! – tchrist

+6

@tchrist:私は「なぜ」に答えなかったのですが、真剣に、Javaの作成者だけがあなたに決定的な答えを与えることができるので、おそらくそれらに到達しようとするべきです。私たちの残りの部分。 – ninjalj

8

もう1つ質問があります。Javaで識別子に制御文字を使用できないのはなぜですか。

言語やその他のシステムを設計する際の良い原則は、それがどのように使用されるか分からず、実装者とユーザーが争う必要のないルールが少ないほどよい理由がない限り禁止しないことです。

実際に変数名にエスケープを埋め込むことで、これを利用してはいけません。ヌル文字を含むクラスを公開する一般的なライブラリは表示されません。

確かに、これは悪用される可能性がありますが、適切な字下げや適切に選択された変数名を強制することよりも、プログラマをこのように自分自身から守ることは言語設計者の仕事ではありません。

+7

@Avi:Javaで識別子に制御文字を使用できない理由はたくさんあります。まず、制御文字は* ID_Start文字ではなく、ID_Continue文字ではありません。** ** ** ** * Default_Ignorable_Code_Point文字ではありません。もう一つは、目に見えないものです。そして3分の1の間、これはちょっとひどいです。それは悪いデザインです! 2つの不等な識別子は、たとえ同じでないとしても同じであるとみなされます。どのような混乱! – tchrist

+4

まず、IS_StartとID_Continue AFAIKは、Unicode 5.0の附属書であるUAX#31で定義されました。その時点でのJavaの有効な文字への変更は、後方互換性のない不要な変更になりました。 – Avi

+0

第2に、2つの不等な識別子をどのように等しく扱うことができるかわかりません。あなたが持つことができるのは、見た目が同じで、扱いが異なる2つの識別子です。ギリシャ文字mu(U + 03BC)は数学記号ミクロン(U + 00B5)と同じように見えますが、これは文字と数字と数学記号だけを許可しても当てはまります。 – Avi

-2

大したことはありません。とにかくそれはあなたにどのような影響を与えますか?

開発者がコードを難読化したい場合は、ASCIIで行うことができます。

開発者がコードを理解できるようにしたい場合、彼は業界の言語フランス語を使用します。識別子はASCIIだけでなく、一般的な英語の単語からも得られます。さもなければ、誰も彼のコードを使用したり読むことはできません。彼は彼が好きなクレイジーなキャラクターを使うことができます。

+12

アイオワ州のおじいちゃんにとって英語とASCIIが十分に良いというこの考えは、ニューデリーのRajeshにとって、京都では、深刻な問題を抱えています。それは帝国主義であり、謙虚である。それは他の人の感性よりもすぐに実行されます。 Unicodeを使用してJavaをデフォルトキャラクタセットに戻し、ASCIIが改善と見なされた1960年代に戻るためにロビーに戻りましょう。 – tchrist

+2

Unicodeを見ると、ASCIIが改善される可能性が増します。国民のキャラクターを使用させることは、外国人と決して協力しない限り、良いアイデアかもしれません。ほとんどの人にとって、ギリシャ文字やアクセント付きラテン文字を書く方法はありません。ほとんどの外国人文字は、ほとんどの人が読むことができません。では、プログラミング言語ではどういったものが良いのでしょうか? – maaartinus

+2

ニューデリーのラジジェスはあなたにとても怒っています。コミュニケーション以外の言語のポイントは何ですか?何百万人ものプログラマーがJavaを話すのはなぜですか?なぜ誰も新しい言語を発明しないのですか? – irreputable

関連する問題