Unicode metni işlemek için iki aşama vardır. Birincisi "bilgiyi kaybetmeden nasıl girebilir ve çıktısını alabilirim" dir. İkincisi, "yerel dil kurallarına göre metni nasıl ele alırım" dır.
tchrist'in yazısı her ikisini de kapsar, ancak ikinci bölüm, yazısındaki metnin% 99'unun geldiği yerdir. Çoğu program G / Ç'yi doğru işlemez, bu nedenle normalleştirme ve harmanlama konusunda endişelenmeye başlamadan önce bunu anlamak önemlidir.
Bu yazı ilk sorunu çözmeyi amaçlıyor
Perl'e veri okuduğunuzda, kodlamanın ne olduğu umurumda değil. Biraz bellek ayırır ve baytları orada saklar. Eğer söylerseniz print $str
, bu baytları terminalinize böler, bu da muhtemelen ona yazılan her şeyin UTF-8 olduğunu varsayar ve metniniz görünür.
Harikulade.
Dışında, değil. Verilere metin olarak davranmaya çalışırsanız Kötü Bir Şey olduğunu göreceksiniz. Sen öteye gitmek gerek length
dizenizle ne hakkında Perl dizenizle düşünür ve ne düşünmek katılmıyorum olduğunu görmek. Gibi bir astar yazın: perl -E 'while(<>){ chomp; say length }'
yazın 文字化け
ve 12 ... doğru cevabı değil, 4.
Çünkü Perl, dizenizin metin olmadığını varsayar. Size doğru cevabı vermeden önce metin olduğunu söylemelisiniz.
Bu yeterince kolay; Kodlama modülünün bunu yapacak işlevleri vardır. Genel giriş noktası Encode::decode
(veya use Encode qw(decode)
elbette). Bu işlev dış dünyadan bir dizi alır ("8-bit bayt" demenin bir yolu olan "sekizli" olarak adlandıracağız) ve Perl'in anlayacağı bir metne dönüştürür. İlk argüman, "UTF-8" veya "ASCII" veya "EUC-JP" gibi bir karakter kodlama adıdır. İkinci argüman dizedir. Dönüş değeri, metni içeren Perl skaleridir.
(Ayrıca Encode::decode_utf8
kodlama için UTF-8 olduğunu varsayar.)
Tek astarımızı yeniden yazarsak:
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
Biz 文字 化 け yazıp sonuç olarak "4" alırız. Başarı.
Bu, Perl'deki Unicode sorunlarının% 99'una çözüm.
Anahtar, programınıza herhangi bir metin geldiğinde, kodunu çözmeniz gerekir. İnternet karakter iletemez. Dosyalar karakterleri depolayamaz. Veritabanınızda karakter yok. Sadece sekizlileri vardır ve sekizlilere Perl'de karakter olarak davranamazsınız. Kodlama modülüyle kodlanmış sekizlileri Perl karakterlerine deşifre etmelisiniz.
Sorunun diğer yarısı, programınızdan veri almaktır. Bunu yapmak kolaydır; sadece use Encode qw(encode)
, verilerinizin kodlanmasının ne olacağına karar verin (UTF-8, UTF-8'i anlayan terminallere, Windows'taki dosyalar için UTF-16 vb.) ve sonra çıktı almak encode($encoding, $data)
yerine sonucunu çıktılayın $data
.
Bu işlem, Perl'in programınızın üzerinde çalıştığı karakterlerini dış dünya tarafından kullanılabilecek sekizlilere dönüştürür. İnternet üzerinden veya terminallerimize sadece karakter gönderebilmemiz çok daha kolay olurdu, ancak yapamayız: sadece sekizli. Bu yüzden karakterleri sekizlilere dönüştürmeliyiz, aksi takdirde sonuçlar tanımsızdır.
Özetlemek gerekirse: tüm çıkışları kodlayın ve tüm girişlerin kodunu çözün.
Şimdi bunu biraz zorlaştıran üç konu hakkında konuşacağız. Birincisi kütüphaneler. Metni doğru kullanıyorlar mı? Cevap ... deniyorlar. Bir web sayfası indirirseniz, LWP sonucunuzu metin olarak geri verecektir. Sonuçta doğru yöntemi çağırırsanız, bu (ve öyle decoded_content
değil content
, sadece sunucudan aldığı sekizli akıştır). Veritabanı sürücüleri kesintili olabilir; DBD :: SQLite'ı sadece Perl ile kullanırsanız, işe yarayacaktır, ancak başka bir araç veritabanınızda UTF-8 dışında bir kodlama olarak depolanan metni koyduysa ... iyi ... doğru şekilde işlenmeyecek doğru işlemek için kod yazana kadar.
Verilerin çıktısını almak genellikle daha kolaydır, ancak "baskıda geniş karakter" görürseniz kodlamayı bir yere karıştırdığınızı bilirsiniz. Bu uyarı "hey, Perl karakterlerini dış dünyaya sızdırmaya çalışıyorsun ve bu hiç mantıklı değil" anlamına geliyor. Programınız çalışıyor gibi görünüyor (çünkü diğer uç genellikle ham Perl karakterlerini doğru bir şekilde işliyor), ancak çok bozuk ve her an çalışmayı durdurabilir. Açık bir şekilde düzeltin Encode::encode
!
İkinci sorun UTF-8 kodlu kaynak kodudur. use utf8
Her dosyanın üstünde söylemediğiniz sürece , Perl kaynak kodunuzun UTF-8 olduğunu varsaymaz. Bu, her seferinde bir şey söylediğinizde my $var = 'ほげ'
, programınıza her şeyi korkunç bir şekilde kıracak çöp enjekte ettiğiniz anlamına gelir . Sen "kullanım utf8" gerekmez, ama bilmiyorsun, sen gerekir programınızda herhangi ASCII olmayan karakterleri kullanmayın.
Üçüncü sorun, Perl'in Geçmiş'i nasıl ele aldığıdır. Uzun zaman önce, Unicode diye bir şey yoktu ve Perl her şeyin Latin-1 metin veya ikili olduğunu varsaydı. Veriler programınıza girdiğinde ve metin olarak ele almaya başladığınızda, Perl her sekizliyi bir Latin-1 karakteri olarak ele alır. Bu yüzden, "文字 化 け" uzunluğunu sorduğumuzda, 12 tane aldık. Perl, Latin-1 dizesi "æååã" (bazıları yazdırılamayan 12 karakter) üzerinde çalıştığımızı varsaydı.
Buna "örtük yükseltme" denir ve bunu yapmak oldukça makul bir şeydir, ancak metniniz Latin-1 değilse istediğiniz şey değildir. Bu yüzden girdinin kodunu açıkça çözmek önemlidir: Bunu yapmazsanız, Perl yapar ve yanlış yapabilir.
İnsanlar, verilerinin yarısının uygun bir karakter dizesi olduğu ve bazılarının hala ikili olduğu durumlarda sorun yaşarlar. Perl, hala ikili olan kısmı Latin-1 metni gibi yorumlayacak ve daha sonra doğru karakter verileriyle birleştirecektir. Bu, karakterlerinizi doğru bir şekilde programınızı kırmış gibi gösterecektir, ancak gerçekte, yeterince düzeltmediniz.
İşte bir örnek: UTF-8 kodlu metin dosyasını okuyan bir programınız var, PILE OF POO
her satıra bir Unicode takıyorsunuz ve yazdırıyorsunuz. Sen şöyle yaz:
while(<>){
chomp;
say "$_ 💩";
}
Ardından, UTF-8 ile kodlanmış bazı veriler üzerinde çalıştırın:
perl poo.pl input-data.txt
UTF-8 verilerini her satırın sonunda bir kaka ile yazdırır. Mükemmel, programım çalışıyor!
Ama hayır, sadece ikili birleştirme yapıyorsun. Dosyadan sekizli okuyorsunuz, bir \n
chomp ile a'yı kaldırıyorsunuz ve daha sonra PILE OF POO
karakterin UTF-8 temsilindeki baytlara yapışıyorsunuz . Programınızı verileri dosyadan kodunu çözmek ve çıktıyı kodlamak için gözden geçirdiğinizde, kaka yerine çöp aldığınızı ("ð ©") fark edeceksiniz. Bu, giriş dosyasının kodunun çözülmesinin yanlış bir şey olduğuna inanmanıza neden olacaktır. Değil.
Sorun şu ki, kaka örtük olarak latin-1 olarak yükseltiliyor. Eğer varsa use utf8
ikili yerine edebi metin yapmak, o zaman tekrar çalışacaktır!
(Bu, Unicode'lu insanlara yardım ederken gördüğüm bir numaralı problem. Haklıydılar ve programlarını bozdular. Tanımlanmamış sonuçlar için üzücü olan bu: Uzun süre çalışan bir programa sahip olabilirsiniz, ancak onarmaya başladığınızda, Endişelenmeyin; programınıza kodlama / kod çözme ifadeleri ekliyorsanız ve kırılırsa, sadece daha fazla işiniz olduğu anlamına gelir.Bir dahaki sefere, Unicode'u akılda tutarak tasarım yaptığınızda, daha kolay!)
Perl ve Unicode hakkında bilmeniz gereken her şey bu. Perl'e verilerinizin ne olduğunu söylerseniz, tüm popüler programlama dilleri arasında en iyi Unicode desteğine sahiptir. Ne tür bir metni beslediğinizi sihirli bir şekilde bileceğini varsayarsanız, verilerinizi geri dönülmez bir şekilde çöpe atacaksınız. Programınızın bugün UTF-8 terminalinizde çalışması, yarının UTF-16 kodlu bir dosyada çalışacağı anlamına gelmez. Şimdi güvenli hale getirin ve kullanıcılarınızın verilerini çöpe atmanın baş ağrısından kurtulun!
Unicode'un işlenmesinin kolay kısmı çıktıyı ve kod çözme girişini kodlamaktır. Zor kısım, tüm giriş ve çıkışlarınızı bulmak ve hangi kodlamayı olduğunu belirlemektir. Ama bu yüzden büyük paralar alıyorsunuz :)