Java kodunun neden bazı Unicode karakterlerle yorumlarda yürütülmesine izin veriliyor?


1356

Aşağıdaki kod "Merhaba Dünya!" (hayır, gerçekten deneyin).

public static void main(String... args) {

   // The comment below is not a typo.
   // \u000d System.out.println("Hello World!");
}

Bunun nedeni, Java derleyicisinin Unicode karakterini \u000dyeni bir satır olarak ayrıştırması ve dönüştürülmesi:

public static void main(String... args) {

   // The comment below is not a typo.
   //
   System.out.println("Hello World!");
}

Böylece bir yorum "yürütme" ile sonuçlanır.

Bu, kötü amaçlı kodu "gizlemek" için veya kötü bir programcının düşünebileceği her şey için kullanılabileceğinden, yorumlarda neden izin verilir ?

Java belirtimine neden izin verilir?


44
“Buna neden izin veriliyor” bana göre çok fikirli. Dil tasarımcıları bir karar verdi, bilmeniz gereken başka ne var? Bu kararı veren kişinin ifadesini bulamazsanız, sadece spekülasyon yapabiliriz.
Ingo Bürk

194
İlginç bir şey, en azından OP'nin
IDE'sinin


47
@Tobb Ama Java tasarımcıları SO'yu ziyaret ediyor, bu yüzden bunlardan biri tarafından cevap almak mümkün . Ayrıca bu soruya zaten cevap veren kaynaklar da olabilir.
Pshemo

41
Basit cevap, kodun dil kurallarına göre hiç bir yorumda bulunmamasıdır, bu nedenle soru yanlış biçimlendirilmiştir.
user207421

Yanıtlar:


741

Unicode kod çözme, diğer sözcüksel çevirilerden önce gerçekleşir. Bunun en önemli yararı, ASCII ile diğer kodlamalar arasında ileri ve geri gitmeyi önemsiz hale getirmesidir. Yorumların nerede başladığını ve bittiğini bulmanıza bile gerek yok!

JLS Bölüm 3.3'te belirtildiği gibi, bu ASCII tabanlı herhangi bir aracın kaynak dosyalarını işlemesine izin verir:

[...] Java programlama dili, bir programı ASCII tabanlı araçlar tarafından işlenebilecek bir forma dönüştüren Unicode'da yazılmış bir programı ASCII'ye dönüştürmenin standart bir yolunu belirtir. [...]

Bu, Java platformu için her zaman önemli bir hedef olan platform bağımsızlığı (desteklenen karakter kümelerinin bağımsızlığı) için temel bir garanti verir.

Herhangi bir Unicode karakterini dosyanın herhangi bir yerine yazabilmek düzgün bir özelliktir ve özellikle latin olmayan dillerde kod belgelendirilirken yorumlarda önemlidir. Anlambilime bu kadar ince şekillerde müdahale edebilmesi, sadece (talihsiz) bir yan etkidir.

Bu tema üzerinde birçok gotcha var ve Joshua Bloch ve Neal Gafter tarafından Java Puzzlers aşağıdaki varyantı dahil etti:

Bu yasal bir Java programı mı? Öyleyse, ne yazdırıyor?

\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

(Bu program sade bir "Merhaba Dünya" programıdır.)

Bilinmezin çözümünde aşağıdakilere dikkat çekiyorlar:

Daha ciddisi, bu bulmaca önceki üçün derslerini güçlendirmeye yarar: Programınıza başka bir şekilde temsil edilemeyen karakterleri eklemeniz gerektiğinde Unicode kaçışları çok önemlidir. Diğer tüm durumlarda bunlardan kaçının.


Kaynak: Java: Yorumlarda kod yürütülüyor mu ?!


84
Kısacası, Java bilerek izin verir: "hata" OP IDE?
Bathsheba

60
@Bathsheba: Daha çok insanların kafasında. İnsanlar Java ayrıştırma işleminin nasıl çalıştığını anlamaya çalışmadığından, IDE'ler bazen kodu yanlış bir şekilde görüntüler. Yukarıdaki örnekte, yorum ile bitmeli \u000dve sonraki kısımda kod vurgulamaları olmalıdır.
Aaron Digulla

62
Başka bir yaygın hata, geçerli bir Unicode kaçış dizisi olmadığı için Windows yollarını koda // C:\user\...derleme hatasına yol açan gibi yapıştırmaktır \user.
Aaron Digulla

50
Tutulma durumunda, Kod \u000dkısmen vurgulanır. Ctrl + Shift + F tuşlarına bastıktan sonra karakter yeni satırla değiştirilir ve satır geri kalanı sarılır
bluelDe

20
@TheLostMind Cevabı doğru anlarsam, bunu blok yorumlarıyla da yeniden üretebilmelisiniz. \u002A/yorumu bitirmelidir.
Taemyr

141

Bu henüz ele alınmadığından, burada bir açıklama, Unicode'un kaçmasının neden diğer kaynak kod işlemlerinden önce gerçekleştiğini açıklayın:

Bunun arkasındaki fikir, Java kaynak kodunun farklı karakter kodlamaları arasında kayıpsız çevirilere izin vermesiydi. Bugün, yaygın Unicode desteği var ve bu bir sorun gibi görünmüyor, ancak o zamanlar Batı ülkelerinden bir geliştiricinin Asya meslektaşlarından Asya karakterleri içeren bazı kaynak kodları alması, bazı değişiklikler yapması kolay değildi ( derleme ve test etme dahil) ve sonucu bir şeye zarar vermeden geri gönderme.

Böylece, Java kaynak kodu herhangi bir kodlamada yazılabilir ve tanımlayıcılar, karakter ve Stringdeğişmez değerler ve yorumlar içinde çok çeşitli karakterlere izin verir . Ardından, kayıpsız bir şekilde aktarmak için, hedef kodlama tarafından desteklenmeyen tüm karakterlerin yerine Unicode çıkışları gelir.

Bu tersine çevrilebilir bir süreçtir ve ilginç olan nokta, çeviri kuralı ona bağımlı olmadığından, Java kaynak kodu sözdizimi hakkında hiçbir şey bilmesi gerekmeyen bir araç tarafından yapılabilmesidir. Bu, derleyici içindeki gerçek Unicode karakterlerine çeviri Java kaynak kodu sözdizimine de bağımsız olarak gerçekleşir. Kaynak kodun anlamını değiştirmeden her iki yönde de rastgele sayıda çeviri adımı uygulayabileceğiniz anlamına gelir.

Bahsetmediğimiz başka bir garip özelliğin nedeni de budur: \uuuuuuxxxxsözdizimi:

Bir çeviri aracı karakterleri kaçan ve halihazırda kaçan bir dizi olan bir diziyi karşılaştığında olduğunda, ek eklemek gerekir udönüştürerek, dizi içine \ucafekadar \uucafe. Anlam değişmez, ancak diğer yöne dönüştürülürken, araç sadece birini kaldırmalı uve yalnızca tekli dizileri uUnicode karakterleriyle değiştirmelidir. Bu şekilde, Unicode kaçışları bile ileri geri dönüştürülürken orijinal formlarında tutulur. Sanırım hiç kimse bu özelliği kullanmadı…


1
İlginçtir, sözdizimini native2asciikullanmıyor gibi görünüyor \uu...xxxx,
ninjalj

5
Evet, native2asciikaynak paketlerinin Properties.loadsadece latin-1'i okumaya sabitlendiği gibi iso-latin-1'e dönüştürerek hazırlanmasına yardımcı olması amaçlanmıştır . Ve orada kurallar farklı, \uuu…sözdizimi ve erken işleme aşaması yok. Özellik dosyalarında, property=multi\u000alinegerçekten aynıdır property=multi\nline. (Belgelerin “Java ™ Dil Spesifikasyonu Bölüm 3.3'te tanımlandığı gibi Unicode kullanımı kaçıyor”
Holger

10
Bu tasarım amacına siğillerden herhangi biri olmadan ulaşılmış olabileceğini unutmayın; en kolay yol \u, U + 0000-007F aralığında karakterler oluşturmak için kaçışları yasaklamak olurdu . (Bu tür karakterlerin tümü, 1990'larda alakalı olan tüm ulusal kodlamalar tarafından yerel olarak temsil edilebilir - belki de bazı kontrol karakterleri dışında, ancak yine de Java yazmak için bunlara ihtiyacınız yoktur.)
zwol

3
@zwol: Eh, yine de Java kaynak kodu içinde izin verilmeyen kontrol karakterlerini hariç tutarsanız, haklısınız. Bununla birlikte, kuralları daha karmaşık hale getirme anlamına gelir. Ve bugün, kararı tartışmak için çok geç…
Holger

ah latin veya başka bir şey değil, utf8 bir belgeyi kaydetme sorunu. Tüm veritabanlarım da bu batılı saçmalık yüzünden kırıldı
David 天宇 Wong

106

Tamamen etkisiz bir şekilde nokta ekleyeceğim, çünkü kendime yardımcı olamıyorum ve henüz yapıldığını görmedim, yanlış olan gizli bir öncül içerdiği için sorunun geçersiz olduğu, yani kodun bir yorum!

Java kaynak kodunda \ u000d, ASCII CR karakterine her şekilde eşdeğerdir. Nerede olursa olsun, sonlu, sade ve basit bir çizgidir. Sorudaki biçimlendirme yanıltıcıdır, bu karakter dizisinin sözdizimsel olarak neye karşılık geldiği şöyledir:

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

IMHO bu nedenle en doğru cevaptır: kod bir yorumda olmadığı için yürütülür; bir sonraki satırda. Tıpkı beklediğiniz gibi "yorumlarda kod yürütülmesine" izin verilmiyor.

Karışıklıkların çoğu, sözdizimi vurgulayıcılarının ve IDE'lerin bu durumu dikkate alacak kadar sofistike olmamalarından kaynaklanmaktadır. Ya unicode kaçışlarını hiç işlemezler ya da eskisi gibi kodu ayrıştırdıktan sonra yaparlar javac.


6
Kabul ediyorum, bu bir java "tasarım hatası" değil, ama bir IDE hatası.
bvdb

3
Soru, daha ziyade , dilin bu özel yönüne aşina olmayan ve belki de sözdizimi vurgulamaya referans vermeyen bir kişiye yorum gibi görünen kodun aslında bir yorum olmadığıdır . Sorunun geçersiz olmasına dayanarak itiraz etmek iğrençtir.
Phil

@Phil: sadece belirli araçlarla görüntülendiğinde bir yorum gibi görünüyor, diğerleri bunu aksini gösteriyor.
jmoreno

1
@jmoreno, kodu okumak için bir metin düzenleyicisinden başka bir şeye sahip olmak zorunda olmamalıdır . En azından, en azından sürpriz prensibini ihlal eder, yani // stil yorumlarının bir sonraki \ n karakterine kadar devam eder - sonuçta \ n ile değiştirilen başka bir diziye değil. Yorumların asla çıkarılmasından başka bir şey olması beklenmez. Kötü önişlemci.
Phil

69

\u000dÇünkü kaçış bir yorum sonlandırır \ukaçar eşit karşılık gelen Unicode karakterleri dönüştürülür önce programı simgeleþtirilmiþ edilir. Sen eşit kullanabilirsiniz \u0057\u0057yerine //etmek başlayacak bir yorum.

Bu, IDE'nizde, söz \u000dkonusu yorumun sona erdiğini açıkça belirtmek için sözdizimini vurgulaması gereken bir hatadır .

Bu aynı zamanda dilde bir tasarım hatasıdır. Şimdi düzeltilemez, çünkü bu ona bağlı programları kırabilir. \ukaçışlar ya derleyici tarafından karşılık gelen Unicode karakterine dönüştürülmeli, ancak bu "mantıklı" bağlamlarda (dize değişmezleri ve tanımlayıcıları ve muhtemelen başka hiçbir yerde) ya da U + 0000-007F aralığında karakter üretmeleri yasaklanmış olmalıdır , ya da her ikisi de. Bu semantiklerden herhangi biri \u000d, \ukaçışların yararlı olduğu durumlara müdahale etmeden yorumun kaçış tarafından sonlandırılmasını önleyecektir ; bunun ,\u yorumların içindeki kaçışların Latin olmayan bir komut dosyasında yorumları kodlamanın bir yolu olarak kullanılmasını içerdiğini unutmayın . metin düzenleyici nerede daha geniş bir görüş alabilir\ukaçışlar derleyiciden daha önemlidir. ( Yine de \u, herhangi bir bağlamda karşılık gelen karakterler olarak kaçışları görüntüleyecek herhangi bir editör veya IDE'nin farkında değilim .)

C ailesinde benzer bir tasarım hatası var, burada 1 ters eğik çizgi-yeni satırın yorum sınırları belirlenmeden önce işlendiği, ör.

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

Bu özel tasarım hatasını yapmanın kolay olduğunu göstermek için bunu gündeme getiriyorum ve eğer tokenizasyonu düşünmeye ve derleyici programcılarının düşünme biçimini ayrıştırmaya alışkınsanız, düzeltmek için çok geç olana kadar bir hata olduğunu fark etmiyorum. belirleme ve ayrıştırma hakkında. Temel olarak, resmi dilbilginizi zaten tanımladıysanız ve daha sonra birisi sözdizimsel bir özel durum ortaya çıkarırsa - trigraflar, ters eğik çizgi, ASCII ile sınırlı kaynak dosyalarında rastgele Unicode karakterleri kodlamak, ne olursa olsun - kamalanması gerekir, tokenizörün önüne , bu özel durumu kullanmanın mantıklı olduğu yere dikkat etmek için tokenizörü yeniden tanımlamaktan daha fazla bir dönüşüm geçişi ekleyin .

1 Pedallar için: C'nin bu yönünün% 100 kasıtlı olduğunun farkındayım, mantıklı - bunu telafi etmiyorum - kodu delinmiş kartlara keyfi olarak uzun çizgilerle mekanik olarak zorla sığdırmanıza izin verecektir. Hala yanlış bir tasarım kararıydı.


17
Bunun bir tasarım hatası olduğunu söyleyecek kadar ileri gitmem . Bunun kötü bir tasarım seçimi veya talihsiz sonuçları olan bir seçim olduğunu kabul edebilirim, ancak yine de dil tasarımcılarının amaçladığı gibi çalıştığını düşünüyorum: ASCII kodlamasını korurken, herhangi bir unicode karakterini dosyanın herhangi bir yerinde kullanmanızı sağlar dosya.
aioobe

12
Bununla birlikte, işleme aşamasının seçiminin, \uC'nin sekizlik gösterim için önde gelen sıfırları kullanma konusundaki liderliğini takip etme kararından daha az saçma olduğunu düşünüyorum . Sekizli gösterim bazen yararlı olsa da, henüz kimsenin önde gelen sıfırın bunu göstermenin iyi bir yolu olduğunu iddia etmesini duymadım.
supercat

3
@supercat Bu özelliği C89'a atananlar, bir özelliği sıfırdan tasarlamak yerine orijinal K&R ön işlemcisinin davranışını genelleştiriyorlardı. Onlar delikli kart en iyi uygulamalarla tanıdık şüphe ve ben de şüphe özellik olduğunu hiç onun belirtilen amaç için kullanılmıştır, bir ya da iki retrocomputing egzersizleri için belki hariç.
zwol

8
@supercat Java ile \uU + 0000..U + 007F aralığında karakter üretmek yasaklanmış olsaydı, tokenizasyon öncesi dönüşüm olarak bir sorunum olmazdı . Bu, "bu her yerde çalışır" ve "bu ASCII karakterlerini sözdizimsel önemi olan diğer adlarla birleştirir" ifadesinin birleşimini, garip durumdan yanlış düzeye indirgiyor.
zwol

4
On sizin "bilgiçler için": o zaman Elbette tek satırlık açıklama yoktu . C Yeni bir çizgi değil bir deyim terminatör vardır yana, çoğunlukla bildiğim kadarıyla "dize birleştirme" belirleyebilir olarak dışında uzun dizeleri kullanılacak olan K & R var. //
Mark Hurd

22

Bu, Java'nın orijinal tasarımına kadar uzanan kasıtlı bir tasarım seçimiydi.

"Unicode kim yorumlarda kaçar?" Diye soranlara, anadilleri Latin karakter setini kullanan kişiler olduklarını düşünüyorum. Başka bir deyişle, Java'nın özgün tasarımında, insanların bir Java programında yasal olan her yerde, genellikle yorumlar ve dizelerde rastgele Unicode karakterler kullanabilmeleri doğaldır.

Bu tür programların Unicode kaçışlarını yorumlayamadığı ve karşılık gelen glifi görüntüleyemediği kaynak metni görüntülemek için kullanılan programlardaki (IDE'ler gibi) bir eksikliktir.


8
Günümüzde kaynak kodumuz için UTF-8 kullanıyoruz ve Unicode karakterleri doğrudan kullanabiliyoruz, kaçmaya gerek yok.
Paŭlo Ebermann

21

@Zwol ile bunun bir tasarım hatası olduğuna katılıyorum; ama ben daha da eleştirelim.

\uescape, string ve char değişmezlerinde kullanışlıdır; ve var olması gereken tek yer burası. Diğer kaçışlarla aynı şekilde ele alınmalıdır \n; ve tam anlamıyla anlam ifade "\u000A" etmelidir"\n" .

Yorumlarda bulunmanın kesinlikle bir anlamı yok \uxxxx- kimse bunu okuyamaz.

Benzer şekilde, \uxxxxprogramın diğer kısımlarında kullanmanın bir anlamı yoktur . Tek istisna, bazı ascii olmayan karakterleri içermeye zorlanan herkese açık API'lardadır - bunu en son ne zaman gördük?

Tasarımcıların nedenleri 1995'te vardı, ancak 20 yıl sonra bu yanlış bir seçim gibi görünüyor.

(okuyuculara soru - bu soru neden yeni oylar almaya devam ediyor? Bu soru popüler bir yerden mi bağlantılı?)


5
API'larda ASCII olmayan karakterlerin kullanıldığı yerlerde takılmıyorsunuz. Bunu kullanan insanlar var (ben değil), örneğin Asya ülkelerinde. Ve tanımlayıcılarda ASCII olmayan karakterler kullandığınızda, bunları belge yorumlarında yasaklamak çok mantıklı değildir. Bununla birlikte, bir jetonun içinde onlara izin vermek ve bir jetonun anlamını veya sınırını değiştirmelerine izin vermek farklı şeylerdir.
Holger

15
uygun dosya kodlamasını kullanabilirler. neden int \u5431yapabiliyorsun yazint 整
ZhongYu

3
Kodlarını API'larına karşı derlemeniz ve uygun kodlamayı kullanamamanız durumunda ne yapacaksınız ( UTF-81995'te yaygın bir destek olmadığını varsayın ). Tek bir yöntem çağırmanız ve işletim sisteminizin Asya dil destek paketini (hatırlayın, doksanları) bu tek yöntem için yüklemek istemezsiniz ...
Holger

5
Şimdi 1995'ten çok daha açık olan şey, programlamak istiyorsanız İngilizce'yi daha iyi bilmenizdir. Programlama uluslararası bir etkileşimdir ve neredeyse tüm kaynaklar İngilizcedir.
ZhongYu

8
Bunun değiştiğini sanmıyorum. Java'nın belgeleri çoğu zaman İngilizceydi. Bir süre Japonca bir çeviri yapıldı, ancak iki dili korumak, dünyanın tüm yerlileri için sürdürme fikrini desteklemiyor (oldukça çürüttü). Ve bundan önce, tanımlayıcılarda Unicode desteğine sahip ana akım bir dil yoktu. Sanırım biri , yerelleştirilmiş kaynak kodunun bir sonraki büyük şey olduğunu düşündü . Neyse ki söyleyemem , işe yaramadı.
Holger

11

Unicode kaçışlarının neden oldukları gibi uygulandığını cevaplayabilen tek kişi, belirtimi yazan kişilerdir.

Bunun makul bir nedeni, tüm BMP'ye Java kaynak kodunun olası karakterleri olarak izin verme arzusunun olmasıdır. Ancak bu bir sorun yaratır:

  • Herhangi bir BMP karakterini kullanabilmek istiyorsunuz.
  • Herhangi bir BMP charater'ı oldukça kolay bir şekilde girebilirsiniz. Bunu yapmanın bir yolu Unicode kaçışlarıdır.
  • Sözcüksel belirtimin insanların okuması ve yazması kolay olmasını ve uygulanması da oldukça kolay olmasını istersiniz.

Unicode kaçışa girdiğinde bu inanılmaz derecede zordur: bir sürü yeni lexer kuralı oluşturur.

Kolay çıkış yolu iki adımda lexing yapmaktır: önce tüm Unicode kaçışlarını temsil ettiği karakterle arayın ve değiştirin ve sonra ortaya çıkan belgeyi Unicode kaçışları yokmuş gibi ayrıştırın.

Bunun tersi, belirtilmesinin kolay olması, bu nedenle spesifikasyonu kolaylaştırması ve uygulanması kolaydır.

Dezavantajı, örneğin.


2
Veya \ uxxxx kullanımını tanımlayıcılarla, dize değişmezleriyle ve karakter sabitleriyle sınırlandırın. C11 bunu yapar.
ninjalj

ancak ayrıştırıcı kurallarını gerçekten karmaşık hale getirir, çünkü bu şeyleri tanımlayan şey budur, tahmin ettiğim şey bu olmasının nedeninin bir parçasıdır.
Martijn
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.