Oto kod, który znalazłem w Internecie:
class M{public static void main(String[]a){System.out.print(new char[]
{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}
Ten kod drukuje Hello World!
się na ekranie; możesz zobaczyć, jak to działa tutaj . Wyraźnie widzępublic static void main
napisane, ale jest odwrotnie. Jak działa ten kod? Jak to się nawet kompiluje?
Edycja: Wypróbowałem ten kod w IntellIJ i działa dobrze. Jednak z jakiegoś powodu nie działa w Notatniku ++, wraz z cmd. Nadal nie znalazłem rozwiązania tego problemu, więc jeśli ktoś to zrobi, skomentuj poniżej.
java
unicode
right-to-left
Wyimaginowana Dynia
źródło
źródło
M
i również po[]a
: fileformat.info/info/unicode/char/202d/index.htm To się nazywa LEWE DO PRAWEGO OVERRIDEniam diov citats cilbup
brzmi jak przysłowie łacińskie ..Odpowiedzi:
Są tu niewidoczne znaki, które zmieniają sposób wyświetlania kodu. W Intellij można je znaleźć, wklejając kod do pustego łańcucha (
""
), który zastępuje je znakami ucieczki Unicode, usuwając ich efekty i ujawniając kolejność, jaką widzi kompilator.Oto wynik tej kopiuj-wklej:
Znaki kodu źródłowego są przechowywane w tej kolejności, a kompilator traktuje je jako znajdujące się w tej kolejności, ale są wyświetlane inaczej.
Zwróć uwagę na
\u202E
znak, który jest przesłonięciem od prawej do lewej, rozpoczynając blok, w którym wszystkie znaki muszą być wyświetlane od prawej do lewej, oraz\u202D
lewej do prawej, rozpoczynając zagnieżdżony blok, w którym wszystkie znaki są wymuszane w kolejności od lewej do prawej, zastępując pierwszą zmianę.Ergo, gdy wyświetla oryginalny kod,
class M
jest wyświetlane normalnie, ale\u202E
odwraca kolejność wyświetlania wszystkiego od tego do\u202D
, co odwraca wszystko ponownie. (Formalnie wszystko od\u202D
terminatora do wiersza jest dwukrotnie odwracane, raz z powodu\u202D
i raz z resztą tekstu odwróconego z powodu\u202E
, i dlatego ten tekst pojawia się na środku linii zamiast na końcu.) Kierunkowość następnej linii jest obsługiwana niezależnie od pierwszej z powodu zakończenia linii, więc{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}
jest wyświetlana normalnie.Aby zapoznać się z pełnym (niezwykle złożonym, dziesiątkami stron) algorytmem dwukierunkowym Unicode, zobacz Standardowy kod Unicode, załącznik nr 9 .
źródło
Wygląda inaczej ze względu na dwukierunkowy algorytm Unicode . Istnieją dwa niewidoczne znaki RLO i LRO, których używa dwukierunkowy algorytm Unicode do zmiany wyglądu znaków zagnieżdżonych między tymi dwoma metaznakami.
Powoduje to, że wizualnie wyglądają w odwrotnej kolejności, ale rzeczywiste znaki w pamięci nie są odwracane. Możesz przeanalizować wyniki tutaj . Kompilator Java zignoruje RLO i LRO i potraktuje je jako białe znaki, dlatego kompiluje kod.
Uwaga 1: Ten algorytm jest używany przez edytory tekstu i przeglądarki do wizualnego wyświetlania znaków jednocześnie zarówno znaków LTR (angielski), jak i RTL (np. Arabski, hebrajski) jednocześnie - stąd „dwukierunkowy”. Możesz przeczytać więcej o algorytmie dwukierunkowym na stronie Unicode .
Uwaga 2: Dokładne zachowanie LRO i RLO zdefiniowano w sekcji 2.2 algorytmu.
źródło
M\u202E
ia\u202D
, ale te identyfikatory wydają się traktowane jako równoważne zM
ia
. (JLS nie radzi sobie dobrze z wyjaśnianiem tego.)Znak
U+202E
odzwierciedla kod od prawej do lewej, jest jednak bardzo sprytny. Jest ukryty, zaczynając od litery M,Cóż, na początku, kiedy zobaczyłem trudne pytanie: „to rodzaj żartu, stracić kogoś innego czasu”, ale potem otworzyłem swoje IDE („IntelliJ”), stworzyłem klasę i przekroczyłem kod ... i to się skompilowało !!! Spojrzałem więc lepiej i zobaczyłem, że „publiczna pustka statyczna” była zacofana, więc poszedłem tam z kursorem i usunąłem kilka znaków ... A co się stanie? Znaki zaczęły się wymazywać wstecz , więc pomyślałem mmm .... rzadko ... muszę go wykonać ... Więc kontynuuję wykonywanie programu, ale najpierw musiałem go zapisać ... i to wtedy znalazłem to! . Nie mogłem zapisać pliku, ponieważ moje IDE powiedziało, że dla niektórych znaków istnieje inne kodowanie, i wskazuj mi, gdzie to było, Więc zaczynam badania w Google w celu znalezienia specjalnych znaków, które mogłyby wykonać zadanie i to wszystko :)
dwukierunkowy algorytm Unicode i
U+202E
powiązane krótkie wyjaśnienie :Dlaczego stworzyć jakiś algorytm, jak to ?
źródło
Rozdział 3 specyfikacji języka zawiera wyjaśnienie, szczegółowo opisując, w jaki sposób wykonuje się tłumaczenie leksykalne dla programu Java. Co jest najważniejsze dla pytania:
Tak więc program jest zapisany w postaci znaków Unicode, a autor może je uciec,
\uxxxx
na wypadek, gdyby kodowanie pliku nie obsługiwało znaku Unicode, w którym to przypadku jest tłumaczone na odpowiedni znak. Jednym ze znaków Unicode obecnych w tym przypadku jest\u202E
. Nie jest pokazywany wizualnie we fragmencie, ale jeśli spróbujesz przełączyć kodowanie przeglądarki, mogą pojawić się ukryte znaki.Dlatego tłumaczenie leksykalne powoduje deklarację klasy:
co oznacza, że identyfikator klasy to
M\u202E
. Specyfikacja uważa to za ważny identifer:źródło