2013-03-07 13 views
57

Natknąłem się na to (znowu) dzisiaj:Dlaczego niektóre znaki ASCII nie mogą być wyrażone w postaci " uXXXX" w kodzie źródłowym Java?

class Test { 
    char ok = '\n'; 
    char okAsWell = '\u000B'; 
    char error = '\u000A'; 
} 

To nie kompiluje:

Nieprawidłowa stała postać w kolejce 4.

Kompilator wydaje się upierają, że piszę zamiast tego "\ n". Nie widzę powodu, by to irytować.

Czy istnieje logiczne wytłumaczenie dlaczego znaki, które mają specjalną notację (jak \t, \n, \r) musi być wyrażone w tej formie w źródle Java?

+1

Można wymienić wszystkie źródła w kodzie z '\ uXXXX' sekwencje sprawiają, że jest on nieczytelny, ale kompiluje się dobrze, ponieważ przekształca wszystko w tekst przed kompilacją. –

Odpowiedz

85

znaki Unicode są zastępowane przez ich wartości, więc linia jest zastąpiony przez kompilator z:

char error = ' 
'; 

który nie jest ważne stwierdzenie Java.

ta jest podyktowana Language Specification:

Kompilator języka programowania Java („kompilatora Java”) najpierw rozpoznaje Unicode ucieka w swoim wejściu, tłumacząc znaki ASCII \ u, a następnie przez cztery cyfry szesnastkowe do jednostka kodowa UTF-16 (§3.1) wskazanej wartości szesnastkowej i pozostawiając wszystkie pozostałe znaki niezmienione. Reprezentacja znaków pomocniczych wymaga dwóch kolejnych ucieczek Unicode. Ten krok tłumaczenia powoduje sekwencję znaków wejściowych Unicode.

To może prowadzić do zaskakujących rzeczy, na przykład, jest to ważny program Java (zawiera ukrytych znaków Unicode) - courtesy of Peter Lawrey:

public static void main(String[] args) { 
    for (char c‮h = 0; c‮h < Character.MAX_VALUE; c‮h++) { 
     if (Character.isJavaIdentifierPart(c‮h) && !Character.isJavaIdentifierStart(c‮h)) { 
      System.out.printf("%04x <%s>%n", (int) c‮h, "" + c‮h); 
     } 
    } 
} 
+16

+1 Dobrze wiedzieć. Więc jeśli używam 'System.out.print (" Hello ")// \ u000ASystem.out.println (" World ");' drukuje "Hello World", nawet jeśli drugi odcisk jest w komentarzu: D – Pshemo

+5

+1 imponująca wiedza, nauczyłeś mnie dzisiaj czegoś nowego! –

+7

Jesteście pierwsi, jak sądzę, więc przyjąłem to. Chociaż to doskonale wyjaśnia przyczynę za tym, wciąż jestem ciekawy, dlaczego twórcy języków myśleli, że wczesna ekspansja była dobrym pomysłem. Wciąż uderza mnie jako wielkiego WTF. – Durandal

23

Unicode sekwencje jak \u000a otrzymują przez rzeczywiste znaki, które reprezentują, zanim kompilator Java zrobi cokolwiek innego z kodem źródłowym. I tak, program ostatecznie kończy się na

char ch = ' 
'; 

Więc \u000a w kodzie źródłowym zostaje zastąpiona wewnętrznie przez znakiem końca wiersza. Zauważ, że dzieje się to zanim kompilator faktycznie odczyta i zinterpretuje twój kod źródłowy.

Nawiązując do Java Language Specification:

Jest to błąd kompilacji czas na linii terminatora (§3.4) pojawia się po otwarciu 'a przed zamknięciem'.

A jak wszyscy wiemy na pamięć, \n jest line terminator, cytując:

LineTerminator: 
    the ASCII LF character, also known as "newline" 
    the ASCII CR character, also known as "return" 
    the ASCII CR character followed by the ASCII LF character 

innych symboli, które mogą spowodować problemy są \, ' i " na przykład.

+1

Program wygląda jak? – wallyk

+0

Wygląda na to, że jest ich znacznie więcej niż 3: \ u0027 (') \ u005c (\\) również powoduje spustoszenie. \ u0034 (") zrywa literał ciągu. – Durandal

2

Ponieważ kompilator traktuje je tak samo, jak tekst bez zmiany znaczenia.

Jest to prawidłowy kod:

class \u00C9 {} 
4

Opisano to w punkcie 3.3. Unicode Escapes http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html. Javac najpierw znajduje sekwencje \ uxxxx w .java i zastępuje je prawdziwymi znakami, które następnie kompiluje. W przypadku

char error = '\u000A'; 

\ u000A będzie zastąpić newline kod znaku (10), a rzeczywistym tekst będzie

char error = ' 
'; 
Powiązane problemy