Bir metin dizesinin kodlaması nasıl hızlı bir şekilde belirlenir? PHP ve Python Php'de metin kodlamasının belirlenmesi, kodlama eşlemesinin nasıl bulunacağı

  • 03.11.2019

Bir görevle karşı karşıya kaldı - sayfanın / metnin / her neyse kodlamanın otomatik olarak algılanması. Görev yeni değil ve birçok bisiklet zaten icat edildi. Makale, ağda bulunanlara dair küçük bir genel bakış - artı bana göründüğü gibi, değerli bir çözüm gibi kendi teklifimi içeriyor.

1. Neden mb_detect_encoding () olmasın?

Kısacası çalışmıyor.

Hadi bir bakalım:
// Girişte - CP1251 ile kodlanmış Rusça metin $ string = iconv ("UTF-8", "Windows-1251", "Anna Pavlovna'ya gitti, elini öptü, onun yerine parfümlü ve parlak kel yamasını koydu, ve sakince kanepeye oturdu. "); // Bakalım md_detect_encoding() bize ne veriyor. İlk $ strict = FALSE var_dump (mb_detect_encoding ($ string, dizi ("UTF-8"))); // UTF-8 var_dump (mb_detect_encoding ($ string, dizi ("UTF-8", "Windows-1251"))); // Windows-1251 var_dump (mb_detect_encoding ($ string, dizi ("UTF-8", "KOI8-R"))); // KOI8-R var_dump (mb_detect_encoding ($ string, dizi ("UTF-8", "Windows-1251", "KOI8-R"))); // YANLIŞ var_dump (mb_detect_encoding ($ string, dizi ("UTF-8", "ISO-8859-5"))); // ISO-8859-5 var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "Windows-1251", "KOI8-R", "ISO-8859-5"))); // ISO-8859-5 // Şimdi $ katı = DOĞRU var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8"), DOĞRU)); // YANLIŞ var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "Windows-1251"), DOĞRU)); // YANLIŞ var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "KOI8-R"), DOĞRU)); // YANLIŞ var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "Windows-1251", "KOI8-R"), DOĞRU)); // YANLIŞ var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "ISO-8859-5"), DOĞRU)); // ISO-8859-5 var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "Windows-1251", "KOI8-R", "ISO-8859-5"), DOĞRU)); // ISO-8859-5
Gördüğünüz gibi, çıktı tam bir karmaşa. Bir fonksiyonun neden bu şekilde davrandığı net olmadığında ne yaparız? Bu doğru, biz google. Harika bir cevap buldum.

Sonunda mb_detect_encoding () kullanma umutlarını ortadan kaldırmak için mbstring uzantısının kaynak koduna girmeniz gerekir. Öyleyse kollarımızı sıvayalım, gidelim:
// ext / mbstring / mbstring.c: 2629 PHP_FUNCTION (mb_detect_encoding) (... // satır 2703 ret = mbfl_identify_encoding_name (& string, elist, size, strict); ...
Ctrl + tıklama:
// ext / mbstring / libmbfl / mbfl / mbfilter.c: 643 const char * mbfl_identify_encoding_name (mbfl_string * string, enum mbfl_no_encoding * elist, int elistsz, int strict) (const mbfl_encoding * kodlama; kodlama = mbfl_string_); .
Ctrl + tıklama:
// ext / mbstring / libmbfl / mbfl / mbfilter.c: 557 / * * kodlamayı tanımla * / const mbfl_encoding * mbfl_identify_encoding (mbfl_string * string, enum mbfl_no_encoding * elist, int elistsz, int strict) (...
Makaleyi gereksiz kaynaklarla kirletmemek için yöntemin tam metnini yayınlamayacağım. Kim kendileri görmekle ilgileniyor. 593 numaralı satırda eziyet çekiyoruz, aslında karakterin kodlamaya uygun olup olmadığı kontrol ediliyor:
// ext / mbstring / libmbfl / mbfl / mbfilter.c: 593 (* filtre-> filtre_fonksiyonu) (* p, filtre); if (filtre-> bayrak) (kötü ++;)
Tek baytlık Kiril alfabesi için temel filtreler şunlardır:

Windows-1251 (orijinal yorumlar korunmuştur)
// ext / mbstring / libmbfl / filtreler / mbfilter_cp1251.c: 142 / * bunların hepsi şimdi çok çirkin! * / static int mbfl_filt_ident_cp1251 (int c, mbfl_identify_filter * filtre) (if (c> = 0x80 && c)< 0xff) filter->bayrak = 0; başka filtre->

KOI8-R
// ext / mbstring / libmbfl / filtreler / mbfilter_koi8r.c: 142 statik int mbfl_filt_ident_koi8r (int c, mbfl_identify_filter * filtre) (if (c> = 0x80 && c)< 0xff) filter->bayrak = 0; else filtre-> flag = 1; / * değil * / dönüş c; )

ISO-8859-5 (burada her şey çok eğlenceli)
// ext / mbstring / libmbfl / mbfl / mbfl_ident.c: 248 int mbfl_filt_ident_true (int c, mbfl_identify_filter * filtre) (dönüş c;)
Gördüğünüz gibi, ISO-8859-5 her zaman DOĞRU döndürür (YANLIŞ döndürmek için filtre-> bayrak = 1 ayarlamanız gerekir).

Filtrelere baktığımızda her şey yerli yerine oturdu. CP1251, KOI8-R'den ayırt edilemez. ISO-8859-5, kodlamalar listesindeyse, her zaman doğru olarak algılanacaktır.

Genel olarak, başarısız olun. Bu anlaşılabilir bir durumdur - yalnızca karakter kodlarıyla, bu kodlar farklı kodlamalarda kesiştiğinden, genel durumda kodlamayı bulmak imkansızdır.

2. Google'ın verdiği şeyler

Ve Google her türlü pisliği dağıtıyor. Kaynakları buraya bile yazmayacağım, isterseniz kendiniz bir bakın (http://'den sonraki boşluğu kaldırın, linksiz metni nasıl göstereceğimi bilmiyorum):

Http: // deer.org.ua/2009/10/06/1/
http://php.su/forum/topic.php?forum=1&topic=1346

3. Habr ile ara

1) tekrar karakter kodları: habrahabr.ru/blogs/php/27378/#comment_710532

2) bence çok ilginç bir çözüm: habrahabr.ru/blogs/php/27378/#comment_1399654
Bağlantıdaki yorumdaki eksiler ve artılar. Şahsen, bu çözümün yalnızca kodlama tespiti için gereksiz olduğunu düşünüyorum - çok güçlü çıkıyor. İçindeki kodlamanın belirlenmesi bir yan etkidir).

4. Aslında benim kararım

Fikir, önceki bölümdeki ikinci bağlantıya bakarken ortaya çıktı. Fikir şu: Büyük bir Rusça metin alıyoruz, farklı harflerin frekanslarını ölçüyoruz ve bu frekansları kodlamayı tespit etmek için kullanıyoruz. İleriye baktığımda, büyük ve küçük harflerle ilgili sorunlar olacağını hemen söyleyeceğim. Bu nedenle, hem büyük/küçük harfe duyarlı hem de büyük/küçük harfe duyarsız (ikinci durumda, aynı sıklıkta küçük harfe daha da büyük bir harf ekledim ve tüm büyük olanlar). Bu "spektra"da, frekansları 0,001'den küçük olan tüm harfler ve bir boşluk kesilir. İşte Savaş ve Barış'ı düzenledikten sonra elde ettiklerim:

Büyük/küçük harfe duyarlı "spektrum":
dizi ("o" => 0.095249209893009, "e" => 0.06836817536026, "a" => 0.067481298384992, "u" => 0.055995027400041, "n" => 0.052242744063325, .... "e" => 0.002252892226507, "H "=> 0.0021318391371162," P "=> 0.0018574762967903," f "=> 0.0015961610948418," B "=> 0.0014044332975731," O "=> 0.0013188987793209," A "=> 0.0012623590130186," K "=>" 0.001061180 =>79 ,)

Büyük/küçük harfe duyarsız:
dizi ("O" => 0.095249209893009, "o" => 0.095249209893009, "E" => 0.06836817536026, "e" => 0.06836817536026, "A" => 0.067481298384992, "a" => 0.067481298384992, "I" => 0.0559950274000 , "ve" => 0.055995027400041, .... "C" => 0.0029893589260344, "c" => 0.0029893589260344, "u" => 0.0024649163501406, "U" => 0.0024649163501406, "E" => 0.002252892226507, "e" => 0.002252892226507, "F" => 0.0015961610948418, "f" => 0.0015961610948418,)

Farklı kodlamalardaki spektrumlar (dizi anahtarları, karşılık gelen kodlamadaki karşılık gelen karakterlerin kodlarıdır):

Daha öte. Bilinmeyen bir kodlama metni alıyoruz, test edilen her kodlama için mevcut karakterin frekansını buluyoruz ve onu bu kodlamanın “derecelendirmesine” ekliyoruz. Daha yüksek derecelendirmeye sahip bir kodlama, büyük olasılıkla bir metin kodlamasıdır.

$ kodlamaları = dizi ("cp1251" => "specter_cp1251.php" gerektirir, "koi8r" => "specter_koi8r.php" gerektirir, "iso88595" => "specter_iso88595.php" gerektirir); $ enc_rates = dizi (); için ($ ben = 0; $ ben< len($str); ++$i) { foreach ($encodings as $encoding =>$ char_specter) ($ enc_rates [$ kodlaması] + = $ char_specter)]; )) var_dump ($ enc_rates);
Bu kodu kendi başınıza çalıştırmayı denemeyin bile - işe yaramaz. Bunu sözde kod olarak düşünebilirsiniz - Makaleyi dağıtmamak için ayrıntıları atladım. $ char_specter tam olarak pastebin tarafından başvurulan dizilerdir.

Sonuçlar
Tablonun satırları metin kodlamasıdır, sütunlar $ enc_rates dizisinin içeriğidir.

1) $ str = "Rusça metin";
0.441 | 0.020 | 0.085 | Windows-1251
0,049 | 0.441 | 0.166 | KOI8-R
0.133 | 0.092 | 0.441 | ISO-8859-5

Hepsi mükemmel. Gerçek kodlama zaten diğerlerinden 4 kat daha yüksek bir dereceye sahip - bu böyle kısa bir metin için. Daha uzun metinlerde oran yaklaşık olarak aynı olacaktır.


cp1251 | koi8r | iso88595 |
0.013 | 0.705 | 0,331 | Windows-1251
0,649 | 0.013 | 0,201 | KOI8-R
0,007 | 0,392 | 0.013 | ISO-8859-5

Hata! Tam yulaf lapası. Ve çünkü CP1251'deki büyük harfler genellikle KOI8-R'deki küçük harflere karşılık gelir. Ve küçük harfler de büyük harflerden çok daha sık kullanılır. Bu yüzden CP1251'de büyük harfli diziyi KOI8-R olarak tanımlıyoruz.
Büyük/küçük harfe duyarsız yapmaya çalışıyorum

1) $ str = "Rusça metin";
cp1251 | koi8r | iso88595 |
0,477 | 0,342 | 0.085 | Windows-1251
0,315 | 0,477 | 0,207 | KOI8-R
0,216 | 0,321 | 0,477 | ISO-8859-5

2) $ str = "SAPPY RUSYA METNİ";
cp1251 | koi8r | iso88595 |
1.074 | 0.705 | 0,465 | Windows-1251
0,649 | 1.074 | 0,201 | KOI8-R
0,331 | 0,392 | 1.074 | ISO-8859-5

Gördüğünüz gibi, doğru kodlama hem büyük/küçük harfe duyarlı "spektra" (dize az sayıda büyük harf içeriyorsa) hem de büyük/küçük harfe duyarlı olmayanlarla tutarlı bir şekilde öndedir. İkinci durumda, büyük/küçük harfe duyarsız olanlarda, lider elbette o kadar emin değildir, ancak küçük satırlarda bile oldukça kararlıdır. Harflerin ağırlıklarıyla da oynayabilirsiniz - örneğin, onları frekansa göre doğrusal olmayan hale getirin.

5. Sonuç

Konu UTF-8 ile çalışmayı kapsamıyor - burada karakter kodlarını almanın ve bir dizeyi karakterlere bölmenin biraz daha uzun/karmaşık olması dışında temel bir fark yoktur.
Bu fikirler elbette sadece Kiril kodlamalarına genişletilemez - tek soru karşılık gelen dillerin / kodlamaların "tayfında".

not Çok gerekli/ilginç olacaksa - tam olarak çalışan kütüphanenin ikinci bölümünü GitHub'da yayınlayacağım. Gönderideki verilerin böyle bir kütüphaneyi hızlı bir şekilde yazmak ve kendi ihtiyaçlarınızı karşılamak için oldukça yeterli olduğuna inansam da - Rus dili için "spektrum" düzenlenmiştir, gerekli tüm kodlamalara kolayca aktarılabilir.

Bir sorun vardı: UTF-8'e göre bir metin dizesinin kodlaması nasıl hızlı bir şekilde belirlenir? UNICODE kodlamasında dizelerle giderek daha sık çalışmanız gerekir.

UNICODE (UTF-8) kodlamasını WINDOWS kodlamasına (win-1251) dönüştürmeniz gerekip gerekmediğini kontrol etme işlevi aşağıdadır.

İşlev, karakter kod dönüştürme üzerine kurulmasa da oldukça doğru bir yanıt verir.

işlev algılama_my_utf ($ s) ($ s = urlencode ($ s); // bazı durumlarda - fazladan bir işlem (yorum) $ res = "0"; $ j = strlen ($ s); $ s2 = strtoupper ($ s) ; $ s2 = str_replace ("% D0", "", $ s2); $ s2 = str_replace ("% D1", "", $ s2); $ k = strlen ($ s2); $ m = 1 ; if ($ k> 0) ($ m = $ j / $ k; if (($ m> 1.2) && ($ m

Kısaca - fonksiyon açıklaması algılama_my_utf():

  • dönüştürmek (dizeyi özel biçime)
  • gelen dizenin uzunluğunu hesapla
  • dizenin tüm harflerini büyük harfe çevir
  • % D0 ve % D1 özel kodlarını kaldırın
  • yeni satırın uzunluğunu hesapla
  • dara satırının yenisine oranını elde ederiz

Bu oran 1 veya ona yakınsa, giriş dizesinin UNICODE'da kodlanmadığına dair bir şüphe vardır. Bu oran 1.2 ile 2.2 arasındaysa, dizeyi WINDOWS win-1251 kodlamasında güvenle yeniden kodlayabilirsiniz.

Fonksiyonun çıkışında UNICODE veya UNICODE değil, sırasıyla 0 veya 1 var.

İşlev yürütme örnekleri:

Girdi dizesi: РїР?С?С? Р? Р? РС? Р? Р · Р? Р ° Р? РёС? Р ° Р? РёР? Р ° С + РёРё Р? imageready Dönüştürülmüş dize:% D0% BF% D0% BE% D1% %80 D1% 8F% D0% B4% D0% BE% D0% BA% D1% %81 D0% BE% D0% B7% D0% B4% D0 % B0% D0% BD% D0% B8% D1% 8F% D0% B0% D0% BD% D0% B8% D0% BC% D0% B0% D1% %86 D0% B8% D0% B8% 20% D0 % B2 imageready İşlev sonucu: 1 Kodlu ifade: imageready'de animasyon oluşturma sırası

Giriş dizisi: R? C? R? R? R? S

Giriş dizesi: РїС? Р? Р? С? Р ° Р? Р? Р ° С + С "РчР? РёС? РїР? С + Р ° С? Р ° РїР ° Р? Р? С

Giriş dizesi: çizim öğreticisi Dönüştürülmüş dize:% EF% EE% F1% EE% E1% E8% E5% EF% EE% F0% E8% F1% EE% E2% E0% ED% E8% FE İşlev sonucu: 0 Kodlu Cümle : Çizim Eğitimi Bu algoritma, arama motoru trafik istatistikleri hizmetinde gelen çeşitli dizilerle iyi başa çıkıyor.

Sitedeki ilginç materyaller:

  • Sitenin arama ilgisiyle ilgili bir makale. Belki de bazı materyaller 10 yıldır modası geçmiştir, ancak bazı noktalara dikkat etmeye değer.

  • Bağışçı site ile alıcı siteler arasında köprü alışverişi sorununa ilişkin kendi görüşünüz.

  • Başka bir hayat hack. "Balda" oyununda dürüst olmayan oyuncuları yendik. Kolayca genişletilebilen geniş bir kelime tabanı.

Fikir - Piyango

Kodlamayı edinme fikri benim tarafımdan icat edilmedi, ancak ne yazık ki yazara da söyleyemem, çünkü yaklaşık 4 yıl önceydi ve bu bilgiyi nereden aldığım çoktan unutuldu. Yazar, tanımın bir varyantını önerdi ve Python'da 1-2 kodlama için bir örnek gösterdi. Çözümünün sadeliği beni bir kenara bırakmadı ve ben de onu istenilen sonuca göre geliştirdim.
Fikrin özü, kodlama tablolarının kendisinde yatmaktadır. Bildiğiniz gibi, herhangi bir kodlama kendi kod tablosunu içerir ve kodlamanın her karakterine belirli bir değer atanır. Burada kodlama tablolarını göstermeyeceğim, artık bunları internette bulmak oldukça kolay.
Uygulama prensibi aşağıdaki gibidir:
  1. Kontrol edilen metnin "analizinin" sonucunu saklamak için bir dizi değişkeni oluşturulur. Dizinin her elemanı, belirli bir kodlamanın sonucunu içerecektir.
  2. Fonksiyonun girişinde alınan metin sembolik olarak tekrarlanır.
  3. Her karakterden bir sıra (bu karakterin değeri) alınır ve kodlama aralığı ile karşılaştırılır.
  4. Değer bir büyük harf (büyük harf) karakterine denk geliyorsa, bu kodlamanın sonucunu depolayan dizi öğesine 1 değeri eklenir.
  5. Değer küçük (küçük) bir karaktere denk geliyorsa, bu kodlamanın sonucunu depolayan dizi öğesine 3 değeri eklenir.
  6. Bu kodlama, daha doğrusu, dizinin kodlamasıyla ilgili sonucu depolayan ve en fazla puanı alan öğesi, büyük olasılıkla orijinal kodlamadır.
Bu algoritma KOI-8, CP1251 (windows-1251) ve diğerleri gibi tek baytlı kodlamalar için geçerlidir. Ancak, çift baytlı kodlamalar için (benim durumumda UTF-8) bu yaklaşım hatalı bir sonuç verecektir. Öncelikle büyük harf için 5, küçük harf için 7 ekleyerek bu sorunu çözmeye çalıştım.Sonuç daha iyi oldu ama yine de tanıma hataları vardı. Bazı deneylerden sonra, UTF'nin büyük harflerle doğru tanımı için sonuca 10, küçük harf 14 için, yani ilk tahminimden 2 kat daha fazla eklenmesi gerektiği sonucuna vardım. Yine de kodun görsel olarak daha iyi anlaşılması için UTF karakterleri için sırasıyla 5 ve 7 bıraktım ve zaten kontrol sırasında bu değerleri 2 artırıp sonuca ekledim.
Temelde tüm algoritma bu. Hem de gereksiz sıkıntılar olmadan.
Bu işlevin uygulanması için çoğu zaman, elbette, kod tablolarının aranması ve doğru aralıkların düzenlenmesi konusunda öldürüldüm. Bu işlevi ilk yazdığımda gerçek kod tablosunu bulmak oldukça zor olmakla kalmadı, aynı zamanda içindeki karakter aralıkları da rastgele atlıyor. Yine de, daha sonra en alakalı (ve bugüne kadar) kodlamalara karar verdim: UTF-8, CP1251, KOI8-R, IBM866, ISO-8859-5 ve MAC. Bu kodlamalar sizin için yeterli değilse, kodu bu algoritmaya göre tamamlayabilirsiniz.

Sözlerden uygulamaya

Aslında, Python'daki tüm fonksiyon kodu şöyle görünür:

Kodlamalar = ("UTF-8": "utf-8", "CP1251": "windows-1251", "KOI8-R": "koi8-r", "IBM866": "ibm866", "ISO-8859- 5 ":" iso-8859-5 "," MAC ":" mac ",)" "" Metin kodlamasını tanımlama "" "def get_codepage (str = Yok): büyük harf = 1 küçük harf = 3 utfupper = 5 utflower = 7 kod sayfası = () encodings.keys'de () enc için: kod sayfaları = str Hiçbiri değilse ve len (str)> 0: last_simb = 0 simb için str: simb_ord = ord (simb) "" "rusça olmayan karakterler" "" simb_ord ise< 128 or simb_ord >256: last_simb == 208 ve (143 ise) "" "UTF-8" "" ile devam edin< simb_ord < 176 or simb_ord == 129): codepages["UTF-8"] += (utfupper * 2) if (last_simb == 208 and (simb_ord == 145 or 175 < simb_ord < 192)) \ or (last_simb == 209 and (127 < simb_ord < 144)): codepages["UTF-8"] += (utflower * 2) """CP1251""" if 223 < simb_ord < 256 or simb_ord == 184: codepages["CP1251"] += lowercase if 191 < simb_ord < 224 or simb_ord == 168: codepages["CP1251"] += uppercase """KOI8-R""" if 191 < simb_ord < 224 or simb_ord == 163: codepages["KOI8-R"] += lowercase if 222 < simb_ord < 256 or simb_ord == 179: codepages["KOI8-R"] += uppercase """IBM866""" if 159 < simb_ord < 176 or 223 < simb_ord < 241: codepages["IBM866"] += lowercase if 127 < simb_ord < 160 or simb_ord == 241: codepages["IBM866"] += uppercase """ISO-8859-5""" if 207 < simb_ord < 240 or simb_ord == 161: codepages["ISO-8859-5"] += lowercase if 175 < simb_ord < 208 or simb_ord == 241: codepages["ISO-8859-5"] += uppercase """MAC""" if 221 < simb_ord < 255: codepages["MAC"] += lowercase if 127 < simb_ord < 160: codepages["MAC"] += uppercase last_simb = simb_ord idx = "" max = 0 for item in codepages: if codepages >max: max = kod sayfaları idx = öğe dönüş idx
İşlev çağrısı örneği

Kodlamaları yazdır

PHP'ye ne dersin

Hazır bir işlevi Python'dan PHP'ye yeniden yazmak zor değildi. Görünüşü itibariyle, Python'daki ebeveyninden pratik olarak hiçbir farkı yoktur:

/ ** * Metin kodlamasını tanımlayın * @param String $ text Text * @return String Metin kodlaması * / function get_codepage ($ text = "") (if (! Empty ($ text)) ($ utflower = 7; $ utfupper = 5; $ küçük harf = 3; $ büyük harf = 1; $ last_simb = 0; $ karakter kümeleri = dizi ("UTF-8" => 0, "CP1251" => 0, "KOI8-R" => 0, "IBM866" => 0, "ISO-8859-5" => 0, "MAC" => 0,); for ($a = 0; $a< strlen($text); $a++) { $char = ord($text[$a]); // non-russian characters if ($char<128 || $char>256) devam; // UTF-8 if (($ last_simb == 208) && (($ karakter> 143 && $ karakter<176) || $char==129)) $charsets["UTF-8"] += ($utfupper * 2); if ((($last_simb==208) && (($char>175 && $ karakter<192) || $char==145)) || ($last_simb==209 && $char>127 && $ karakter<144)) $charsets["UTF-8"] += ($utflower * 2); // CP1251 if (($char>223 && $ karakter<256) || $char==184) $charsets["CP1251"] += $lowercase; if (($char>191 && $ karakter<224) || $char==168) $charsets["CP1251"] += $uppercase; // KOI8-R if (($char>191 && $ karakter<224) || $char==163) $charsets["KOI8-R"] += $lowercase; if (($char>222 && $ karakter<256) || $char==179) $charsets["KOI8-R"] += $uppercase; // IBM866 if (($char>159 && $ karakter<176) || ($char>223 && $ karakter<241)) $charsets["IBM866"] += $lowercase; if (($char>127 && $ karakter<160) || $char==241) $charsets["IBM866"] += $uppercase; // ISO-8859-5 if (($char>207 && $ karakter<240) || $char==161) $charsets["ISO-8859-5"] += $lowercase; if (($char>175 && $ karakter<208) || $char==241) $charsets["ISO-8859-5"] += $uppercase; // MAC if ($char>221 && $ karakter<255) $charsets["MAC"] += $lowercase; if ($char>127 && $ karakter<160) $charsets["MAC"] += $uppercase; $last_simb = $char; } arsort($charsets); return key($charsets); } }
İşlev çağrısı örneği

Echo get_codepage (file_get_contents ("test.txt"));

LikNo veya makinenin çalışmasına müdahale etmeyin

Bu işlevi çarpışma testi yapmaya çalışmayın. Algoritmadan, girişte ne kadar az metin alırsa, işlevin kodlamayı yanlış tanıma olasılığının o kadar yüksek olduğu açıktır. Öte yandan, Leo Tolstoy'un ciltlerini beslemek de mantıklı değil: Bu yöntem, 100-200 karakterlik küçük bir cümle ile mükemmel bir iş çıkarıyor. Ve giriş çağrısı örneklerinde, kodlamasının belirlenmesi gereken bir metin olduğu varsayıldığı belirli bir "test.txt" dosyasının tüm içeriğini göndermeme rağmen, küçük bir parça metin, işlevin girişine iletilebilir (ve edilmelidir).
Karışık büyük ve küçük harflerle yapılan sapkınlıklar, bu durumda genellikle uygun değildir, çünkü bu yöntem, yaklaşık olarak Rusça bilen sıradan bir sıradan görev için yazılmıştır. Ve bu tür deneyler genellikle bana bir anekdotu hatırlatıyor:

Bir Rus ağaç işleme tesisi bir Japon makinesi satın aldı. Rus işçileri etrafına toplandı ve nasıl çalıştığını anlayalım. Biri tahtayı aldı ve içine itti. Birim: dzzzzzzzzzzzzzzzzzzzz... Çıkışta hazır bir tabure var. Beyler: Kendinize hiçbir şey !!! Ekrandaki birim: ne düşündünüz? Bir diğeri kesilmemiş bir kütük aldı ve üniteye yerleştirdi. Birim: dzzzzzzzzzzzzzzzzzzzz ... Çıkışta bitmiş bir oyma büfe var. Beyler: Kendinize hiçbir şey !!! Ekrandaki birim: ne düşündünüz? Üçüncü adam dayanamadı, bir yerden korkuluğu çekti ve üniteye yapıştırdı. Birim: drrrrr-tyh-tykh-tykh ... Duman ve ekranda şunlar yazıyor: Kendime bir şey yok !!! çocuklar: ne düşündünüz !!!
Bu yüzden bu sapık testler için muhtemelen bu fonksiyon olmayan bir sapık algoritmaya ihtiyacınız var. Uygulamadan da diyeceğim ki 4 yıllık kullanım süresi boyunca bu yöntem beni asla yarı yolda bırakmadı ve her zaman doğru sonucu verdi.
Umarım makalem birilerine faydalı olur.
Dikkatiniz için teşekkürler.

İçeriğin tamamını veya bir kısmını kullanırken kaynağa yani bloguma link vermeyi unutmayın.

Farklı rss beslemelerinden birçok metin okudum ve bunları veritabanıma ekledim.

Tabii ki, örneğin borularda kullanılan birkaç farklı karakter kodlaması vardır. UTF-8 ve ISO-8859-1.

Ne yazık ki, bazen metin kodlamalarıyla ilgili sorunlar olabilir. Örnek:

1) "Fußball"daki "ß", veritabanımda şöyle görünmelidir: "Ÿ". "Ÿ" ise, doğru görüntülenir.

2) Bazen "Fußball"daki "ß" veritabanımda şöyle görünüyor: "ß". O zaman elbette doğru görüntülenmiyor.

3) Diğer durumlarda "ß", "ß" olarak kaydedilir - bu nedenle herhangi bir değişiklik yapılmadan. O zaman da yanlış görüntüleniyor.

2. ve 3. durumlardan kaçınmak için ne yapabilirim?

Aynı kodlamayı, tercihen UTF-8'i nasıl yapabilirim? utf8_encode () işlevini ne zaman kullanmalıyım, ne zaman utf8_decode () kullanmalıyım (etkinin ne olduğu açık, ancak işlevleri ne zaman kullanmalıyım?), Girişle ne zaman bir şey yapmalıyım?

Bana yardım edip aynı kodlamayı nasıl yapacağımı söyler misin? Belki mb-detect-encoding () işleviyle? Bunu yapmak için bir fonksiyon yazabilir miyim? Yani sorunlarım: 1) Metinde hangi kodlamanın kullanıldığını nasıl öğrenebilirim 2) Eski kodlamadan bağımsız olarak UTF-8'e nasıl dönüştürebilirim?

DÜZENLE: Böyle bir işlev işe yarar mı?

Doğru_kodlama ($ metin) işlevi ($ current_encoding = mb_detect_encoding ($ metin, "auto"); $ metin = iconv ($ current_encoding, "UTF-8", $ metin); dönüş $ metin;)

test ettim ama çalışmıyor. O'nun nesi var?

24 cevap

Önceden var olan bir UTF8 dizesine utf8_encode () uygularsanız, bozuk UTF8 çıktısı döndürür.

Tüm bu sorunları gideren bir işlev oluşturdum. Buna Kodlama :: toUTF8 () denir.

Dizelerinizin kodlamasının ne olduğunu bilmenize gerek yok. Latin1 (iso 8859-1), Windows-1252 veya UTF8 olabilir veya dize bunları içerebilir. Kodlama :: toUTF8 () her şeyi UTF8'e dönüştürür.

Bunu yaptım çünkü hizmet bana bir veri akışı veriyordu, hepsi berbattı, UTF8 ve Latin1'i aynı satırda karıştırıyordu.

Kullanım:

Require_once ("Encoding.php"); \ ForceUTF8 \ Kodlama kullanın; // Şimdi isim alanlı. $ utf8_string = Kodlama :: toUTF8 ($ utf8_or_latin1_or_mixed_string); $ latin1_string = Kodlama :: toLatin1 ($ utf8_or_latin1_or_mixed_string);

Bozuk görünen her UTF8 satırını düzeltecek olan Encoding :: fixUFT8 () adlı başka bir işlev ekledim.

Kullanım:

Require_once ("Encoding.php"); \ ForceUTF8 \ Kodlama kullanın; // Şimdi isim alanlı. $ utf8_string = Kodlama :: fixUTF8 ($ garbled_utf8_string);

Yankı Kodlaması :: fixUTF8 ("Fà © dà © ration Camerounaise de Football"); echo Kodlama :: fixUTF8 ("Fà© dà© ration Camerounaise de Football"); echo Kodlama :: fixUTF8 ("FÃÂà© dÃÂà© ration Camerounaise de Football"); echo Kodlama :: fixUTF8 ("Fà© dération Camerounaise de Football");

Fédération Camerounaise de Football Fédération Camerounaise de Football Fédération Camerounaise de Football Fédération Camerounaise de Football

Güncelleme: Fonksiyonu (forceUTF8) Encoding sınıfındaki bir statik fonksiyon ailesine dönüştürdüm. Yeni özellik - Kodlama :: toUTF8 ().

İlk önce hangi kodlamanın kullanıldığını belirlemeniz gerekir. RSS beslemelerini (muhtemelen HTTP üzerinden) ayrıştırdığınız için, kodlamayı HTTP başlığının Content-Type alanının karakter kümesi parametresinden okumalısınız. Eksikse, işleme talimatının kodlama özelliğinden kodlamayı okuyun. Bu da eksikse, Malzeme Listesinde tanımlandığı gibi UTF-8 kullanın.

Değiştirmekİşte muhtemelen yapacağım şey:

Kodlama tespiti zordur.

mb_detect_encoding, girdiğiniz birden çok adaydan tahminde bulunarak çalışır. Bazı kodlamalarda, belirli bayt dizileri geçersizdir, bu nedenle farklı adayları ayırt edebilir. Ne yazık ki, aynı baytların geçerli olduğu (ancak farklı) birçok kodlama vardır. Bu durumlarda, kodlamayı belirlemek imkansızdır; Bu durumlarda tahminde bulunmak için kendi mantığınızı uygulayabilirsiniz. Örneğin, bir Japon sitesinden gelen verilerin Japonca olarak kodlanması muhtemeldir.

Yalnızca Batı Avrupa dilleriyle uğraşırken, üç ana kodlamayı göz önünde bulundurun: utf-8, iso-8859-1 ve cp-1252. Birçok platform için varsayılan olduklarından, büyük olasılıkla yanlış bildirilirler. Örneğin. insanlar farklı kodlamalar kullanırlarsa, bu konuda açık sözlü olmaları muhtemeldir, çünkü aksi takdirde yazılımları çok sık bozulur. Bu nedenle, kodlama bu üçünden biri olarak bildirilmedikçe sağlayıcıya güvenmek iyi bir stratejidir. Yine de mb_check_encoding kullanarak bunun gerçekten geçerli olduğunu ikiye katlamalısınız (geçerlinin olduğu gibi olmadığını unutmayın - aynı giriş birçok kodlama için geçerli olabilir). Bu onlardan biriyse, aralarında ayrım yapmak için mb_detect_encoding'i kullanabilirsiniz. Neyse ki, bu oldukça belirleyici; UTF-8, ISO-8859-1, WINDOWS-1252 olan doğru algılama sırasını kullanmanız yeterlidir.

Kodlamayı keşfettikten sonra, onu dahili temsiline dönüştürmeniz gerekir (utf-8 tek mantıklı seçimdir). utf8_encode işlevi, iso-8859-1'i utf-8'e dönüştürür, böylece yalnızca o belirli giriş türü için kullanılabilir. Diğer kodlamalar için mb_convert_encoding kullanın.

Bu hile sayfası PHP'de UTF-8 kullanımıyla ilgili bazı genel uyarıları listeler: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet

Bir dizedeki çok baytlı karakterleri algılayan bu işlev de yararlı olabilir ():

İşlev algılamaUTF8 ($ dize) (preg_match ("% (?: [\ xC2- \ xDF] [\ x80- \ xBF] # aşırı uzun olmayan 2 bayt | \ xE0 [\ xA0- \ xBF] [\ x80- \ xBF] # fazla uzunluklar hariç | [\ xE1- \ xEC \ xEE \ xEF] [\ x80- \ xBF] (2) # düz 3 bayt | \ xED [\ x80- \ x9F] [\ x80- \ xBF] # vekiller hariç | \ xF0 [\ x90- \ xBF] [\ x80- \ xBF] (2) # uçaklar 1-3 | [\ xF1- \ xF3] [\ x80- \ xBF] (3) # uçaklar 4- 15 | \ xF4 [\ x80- \ x8F] [\ x80- \ xBF] (2) # düzlem 16) +% xs ", $ dize);)

Biraz uyarı, "ß"nin veritabanınızda "Ÿ" olarak görünmesi gerektiğini söylediniz.

Bunun nedeni muhtemelen latin1 karakter kodlamalı bir veritabanı kullanmanız veya php-mysql bağlantısının yanlış yapılandırılmış olmasıdır, php, mysql'nizin utf-8 kullanacak şekilde yapılandırıldığını varsayar, bu nedenle verileri utf8 olarak gönderir, ancak mysql'niz php'nin gönderdiğine inanır iso-8859-1 olarak kodlanmış veriler, bu nedenle gönderilen verilerinizi tekrar utf-8 olarak kodlamaya çalışabilir ve bu gibi sorunlara neden olabilir.

Şuna bir göz atın, size yardımcı olabilir: http://php.net/manual/en/function.mysql-set-charset.php

Yanıtlar farklı kodlamalarla kodlanabileceğinden girişteki kodlamayı kontrol etmeniz gerekir.
Aşağıdaki işlevi kullanarak algılama ve çeviri yaparak tüm içeriği UTF-8'de gönderilmeye zorluyorum:

function fixRequestCharset () ($ ref = dizi (& $ _ GET, & $ _ POST, & $ _ REQUEST); foreach ($ ref as & $ var) (foreach ($ var as $ key => $ val) ($ kodlama = mb_detect_encoding ($ var [$ anahtar], mb_detect_order (), true); if (! $ kodlama) devam ediyor; if (strcasecmp ($ kodlama, "UTF-8")! = 0) ($ kodlama = iconv ($ kodlama, " UTF-8 ", $ var [$ anahtar]); if ($ kodlama === yanlış) devam; $ var [$ anahtar] = $ kodlama;))))

Bu prosedür, uzak ana bilgisayardan gelen tüm PHP değişkenlerini UTF-8'e çevirecektir.
Veya kodlama algılanamıyor veya dönüştürülemiyorsa değeri yok sayın.
İhtiyaçlarınıza göre özelleştirebilirsiniz.
Değişkenleri kullanmadan önce çağırın.

Kodlamanız UTF-8 kodlu gibi görünüyor iki kere; yani, başka bir kodlamayla, UTF-8'de ve yine UTF-8'de. Sanki iso-8859-1'i iso-8859-1'den utf-8'e dönüştürmüş ve UTF-8'e başka bir dönüşüm için yeni satırla iso-8859-1 olarak işlem görmüş gibisiniz.

İşte yaptıklarınızın bazı sözde kodları:

$ inputstring = getFromUser(); $ utf8string = iconv ($current_encoding, "utf-8", $ inputstring); $ kusurlu dize = iconv ($ geçerli_encoding, "utf-8", $ utf8string);

Denemelisin:

  • mb_detect_encoding () veya kullanmak istediğiniz herhangi bir şeyle kodlamayı tespit edin
  • UTF-8 ise, iso-8859-1'e dönüştürün ve 1. adımı tekrarlayın
  • sonunda UTF-8'e geri dön

"Ortalama" dönüşümde iso-8859-1 kullandığınız varsayılmaktadır. Windows-1252 kullandıysanız, Windows-1252'ye (latin1) dönüştürün. Kaynağın kaynak kodlaması önemli değildir; hatalı, ikinci dönüşümde kullandığınız.

Ne olduğuyla ilgili tahminim bu; bir genişletilmiş ASCII baytı yerine dört bayt almak için biraz daha fazlasını yapabilirsiniz.

Almanca ayrıca iso-8859-2 ve windows-1250 (latin2) kullanır.

RSS beslemeleri için karakter kodlamasını tasarlamak karmaşık görünüyor. Normal web sayfaları bile genellikle kodlamalarını atlar veya geri alır.

Bu nedenle, doğru kodlama algılama yöntemini kullanmayı deneyebilir ve ardından bir tür otomatik algılama (tahmin) yöntemine geri dönebilirsiniz.

Bunun eski bir soru olduğunu biliyorum, ancak yararlı bir cevabın asla incitmediğine inanıyorum. Masaüstü uygulamaları, SQLite ve GET/POST değişkenleri arasındaki kodlamamla ilgili sorunlar yaşıyorum. Bazıları UTF-8'de, bazıları ASCII'de olacak ve çoğunlukla yabancı karakterler söz konusu olduğunda kafa karıştırıcı olacak.

İşte benim çözümüm. GET / POST / REQUEST'inizi (çerezleri kaçırdım, ancak gerekirse onları ekleyebilirsiniz) işlemeden önce her sayfa yüklemesinde düzleştirir. Başlıkta iyi çalışıyor. PHP, kaynak kodlamayı otomatik olarak algılayamazsa uyarılar verir, bu nedenle bu uyarılar @ ile bastırılır.

// Veri tabanıyla Nice oynamak için değişkenlerimizdeki her şeyi UTF-8'e dönüştürün ... // Çift kodlama yapmamamıza yardımcı olması için burada biraz otomatik algılama kullanın ... // Kodlama algılanamadığında olası uyarıları @ ile bastırın try ($ işlem = dizi (& $ _ GET, & $ _ POST, & $ _ REQUEST); while (list ($ anahtar, $ değer) = her biri ($ işlem)) (foreach ($ k olarak $ val =>) $ v) (unset ($ process [$ key] [$ k]); if (is_array ($ v)) ($ process [$ key] [@ mb_convert_encoding ($ k, "UTF-8", "auto") ] = $ v; $ işlem = & $ işlem [$ anahtar] [@ mb_convert_encoding ($ k, "UTF-8", "oto")]);) else ($ işlem [$ anahtar] [@ mb_convert_encoding ($ k, "UTF- 8 "," auto ")] = @mb_convert_encoding ($ v," UTF-8 "," auto ");))) unset ($ process);) catch (Exception $ ex) ()

AGES ile kodlama çözümlerini kontrol ettim ve bu sayfa muhtemelen yıllarca süren aramaların sonu! Bahsettiğiniz önerilerden bazılarını kontrol ettim ve işte notlarım:

Bu benim test hattım:

bu, onları görmek için özel chàrs ayarı için kullanmadığım "wròng wrìtten" dizesidir, fùnctìon tarafından dönüştürülmüştür !! nedir!

Sayfamın yazı tipi UTF-8

Bu şekilde INSERT yaparsam, DB'mde muhtemelen Mars'tan gelen bazı karakterler var ... bu yüzden onları "aklı başında" UTF-8'e dönüştürmem gerekiyor. utf8_encode () denedim ama yine de yabancı karakterler veritabanımı istila ediyor ...

Bu yüzden, 8 numarada yayınlanan forceUTF8 işlevini kullanmaya çalıştım, ancak DB'de depolanan dize şöyle görünüyor:

bu "yazılmış" bir dizedir bùt i pù "sà ²me" için özel chè ler görmek için, fùnctìon tarafından dönüştürülen !! nedir!

Bu yüzden, bu sayfada biraz daha bilgi toplayarak ve diğer sayfalardaki diğer bilgilerle birleştirerek sorunu şu çözümle çözdüm:

$ nihayetIDidIt = mb_convert_encoding ($ string, mysql_client_encoding ($ resourceID), mb_detect_encoding ($ string));

Şimdi veritabanımda doğru kodlamaya sahip bir dizgem var.

Not: Sadece mysql_client_encoding işlevine dikkat edin! Bu işlev parametre olarak bir kaynak kimliği gerektirdiğinden bir DB'ye bağlı olmanız gerekir.

Ama tamam, bu kod dönüştürmeyi INSERT'imden önce yapıyorum, bu yüzden bu benim için bir sorun değil.

Umarım bu, bu sayfa gibi birinin bana yardım etmesine yardımcı olur!

Herkese teşekkürler!

mb_detect_encoding ve mb_convert_encoding ile ilgili ilginç olan şey, önerdiğiniz kodlamaların sırasının önemli olmasıdır:

// $ girdisi aslında UTF-8'dir mb_detect_encoding ($ girdisi, "UTF-8", "ISO-8859-9, UTF-8"); // ISO-8859-9 (YANLIŞ!) Mb_detect_encoding ($ girişi, "UTF-8", "UTF-8, ISO-8859-9"); // UTF-8 (Tamam)

Böylece, beklenen kodlamaları belirtirken belirli bir sıra kullanabilirsiniz. Ancak, bunun güvenilir olmadığını unutmayın.

Echo mb_detect_encoding ($ str, "auto");

Echo mb_detect_encoding ($ str, "UTF-8, ASCII, ISO-8859-1");

Sonuçların ne olduğunu gerçekten bilmiyorum, ancak farklı kodlamalara sahip bazı yayınlarınızı almanızı ve mb_detect_encoding'in çalışıp çalışmadığını denemenizi öneririm.

Güncelleme
auto "ASCII, JIS, UTF-8, EUC-JP, SJIS" için kısaltılmıştır. dizeyi iconv ile utf-8'e dönüştürmek için kullanabileceğiniz algılanan kodlamayı döndürür.

Test etmedim, bu yüzden garantisi yok. ve belki daha kolay bir yolu vardır.

Bu sürüm Almanca içindir, ancak $ CHARSETS ve $ TESTCHARS'ı değiştirebilirsiniz.

Class CharsetDetector (özel statik $ CHARSETS = dizi ("ISO_8859-1", "ISO_8859-15", "CP850"); özel statik $ TESTCHARS = dizi ("€", "ä", "Ä", "ö", "Ö", "ü", "Ü", "ß"); public static function convert ($ string) (return self :: __ iconv ($ string, self :: getCharset ($ string));) public static function getCharset ($ string) ($ normalize = self :: __ normalize ($ string); if (! strlen ($ normalleştirilmiş)) "UTF-8" döndürür; $ best = "UTF-8"; $ charcountbest = 0; foreach (self :: $ CHARSETS $ karakter kümesi olarak) ($ str = self :: __ iconv ($ normalleştirilmiş, $ karakter kümesi); $ karakter sayısı = 0; $ stop = mb_strlen ($ str, "UTF-8"); için ($ idx = 0 ; $ idx< $stop; $idx++) { $char = mb_substr($str, $idx, 1, "UTF-8"); foreach (self::$TESTCHARS as $testchar) { if($char == $testchar) { $charcount++; break; } } } if($charcount>$ charcountbest) ($ charcountbest = $ charcount; $ en iyi = $ karakter kümesi;) // echo $ metin. "
";) return $ best;) private static function __normalize ($ str) ($ len = strlen ($ str); $ ret =" "; for ($ i = 0; $ i)< $len; $i++){ $c = ord($str[$i]); if ($c >128) (if (($ c> 247)) $ ret. = $ Str [$ i]; elseif ($ c> 239) $ bayt = 4; elseif ($ c> 223) $ bayt = 3; elseif ($ c> 191) $ bytes = 2; else $ ret. = $ str [$ i]; if (($ i + $ bytes)> $ len) $ ret. = $ str [$ i]; $ ret2 = $ str [$ i]; while ($ bayt> 1) ($ i ++; $ b = ord ($ str [$ i]); if ($ b< 128 || $b >191) ($ ret. = $ Ret2; $ ret2 = ""; $ i + = $ byte-1; $ byte = 1; break;) else $ ret2. = $ Str [$ i]; $ bayt--; ))) $ ret döndür; ) özel statik işlev __iconv ($ dize, $ karakter kümesi) (dönüş simgesiv ($ karakter kümesi, "UTF-8", $ dize);))

PHP betiklerinizi sıraladıktan sonra, mysql'e hangi kodlamayı geçmekte olduğunuzu ve onu almak istediğinizi söylemeyi unutmayın.

Örnek: karakter setini utf8 olarak ayarlamak

latin1 IO oturumunda utf8 verilerini latin1 tablosuna geçirmek bu kötü kuşları verir. Bunu her gün ticaret mağazalarında görüyorum. Geri ve dördüncü doğru görünebilir. Ama phpmyadmin gerçeği gösterecektir. Hangi kodlamayı geçtiğinizi mysql söylemek, sizin için mysql verilerini işleyecektir.

Mevcut şifreli mysql verilerinin nasıl kurtarılacağı başka bir tartışma noktasıdır. :)

Bir görevle karşı karşıya kaldı - sayfanın / metnin / her neyse kodlamanın otomatik olarak algılanması. Görev yeni değil ve birçok bisiklet zaten icat edildi. Makale, ağda bulunanlara dair küçük bir genel bakış - artı bana göründüğü gibi, değerli bir çözüm gibi kendi teklifimi içeriyor.

1. Neden mb_detect_encoding () olmasın?

Kısacası çalışmıyor.

Hadi bir bakalım:
// Girişte - CP1251 ile kodlanmış Rusça metin $ string = iconv ("UTF-8", "Windows-1251", "Anna Pavlovna'ya gitti, elini öptü, onun yerine parfümlü ve parlak kel yamasını koydu, ve sakince kanepeye oturdu. "); // Bakalım md_detect_encoding() bize ne veriyor. İlk $ strict = FALSE var_dump (mb_detect_encoding ($ string, dizi ("UTF-8"))); // UTF-8 var_dump (mb_detect_encoding ($ string, dizi ("UTF-8", "Windows-1251"))); // Windows-1251 var_dump (mb_detect_encoding ($ string, dizi ("UTF-8", "KOI8-R"))); // KOI8-R var_dump (mb_detect_encoding ($ string, dizi ("UTF-8", "Windows-1251", "KOI8-R"))); // YANLIŞ var_dump (mb_detect_encoding ($ string, dizi ("UTF-8", "ISO-8859-5"))); // ISO-8859-5 var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "Windows-1251", "KOI8-R", "ISO-8859-5"))); // ISO-8859-5 // Şimdi $ katı = DOĞRU var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8"), DOĞRU)); // YANLIŞ var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "Windows-1251"), DOĞRU)); // YANLIŞ var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "KOI8-R"), DOĞRU)); // YANLIŞ var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "Windows-1251", "KOI8-R"), DOĞRU)); // YANLIŞ var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "ISO-8859-5"), DOĞRU)); // ISO-8859-5 var_dump (mb_detect_encoding ($ dize, dizi ("UTF-8", "Windows-1251", "KOI8-R", "ISO-8859-5"), DOĞRU)); // ISO-8859-5
Gördüğünüz gibi, çıktı tam bir karmaşa. Bir fonksiyonun neden bu şekilde davrandığı net olmadığında ne yaparız? Bu doğru, biz google. Harika bir cevap buldum.

Sonunda mb_detect_encoding () kullanma umutlarını ortadan kaldırmak için mbstring uzantısının kaynak koduna girmeniz gerekir. Öyleyse kollarımızı sıvayalım, gidelim:
// ext / mbstring / mbstring.c: 2629 PHP_FUNCTION (mb_detect_encoding) (... // satır 2703 ret = mbfl_identify_encoding_name (& string, elist, size, strict); ...
Ctrl + tıklama:
// ext / mbstring / libmbfl / mbfl / mbfilter.c: 643 const char * mbfl_identify_encoding_name (mbfl_string * string, enum mbfl_no_encoding * elist, int elistsz, int strict) (const mbfl_encoding * kodlama; kodlama = mbfl_string_); .
Ctrl + tıklama:
// ext / mbstring / libmbfl / mbfl / mbfilter.c: 557 / * * kodlamayı tanımla * / const mbfl_encoding * mbfl_identify_encoding (mbfl_string * string, enum mbfl_no_encoding * elist, int elistsz, int strict) (...
Makaleyi gereksiz kaynaklarla kirletmemek için yöntemin tam metnini yayınlamayacağım. Kim kendileri görmekle ilgileniyor. 593 numaralı satırda eziyet çekiyoruz, aslında karakterin kodlamaya uygun olup olmadığı kontrol ediliyor:
// ext / mbstring / libmbfl / mbfl / mbfilter.c: 593 (* filtre-> filtre_fonksiyonu) (* p, filtre); if (filtre-> bayrak) (kötü ++;)
Tek baytlık Kiril alfabesi için temel filtreler şunlardır:

Windows-1251 (orijinal yorumlar korunmuştur)
// ext / mbstring / libmbfl / filtreler / mbfilter_cp1251.c: 142 / * bunların hepsi şimdi çok çirkin! * / static int mbfl_filt_ident_cp1251 (int c, mbfl_identify_filter * filtre) (if (c> = 0x80 && c)< 0xff) filter->bayrak = 0; başka filtre->

KOI8-R
// ext / mbstring / libmbfl / filtreler / mbfilter_koi8r.c: 142 statik int mbfl_filt_ident_koi8r (int c, mbfl_identify_filter * filtre) (if (c> = 0x80 && c)< 0xff) filter->bayrak = 0; else filtre-> flag = 1; / * değil * / dönüş c; )

ISO-8859-5 (burada her şey çok eğlenceli)
// ext / mbstring / libmbfl / mbfl / mbfl_ident.c: 248 int mbfl_filt_ident_true (int c, mbfl_identify_filter * filtre) (dönüş c;)
Gördüğünüz gibi, ISO-8859-5 her zaman DOĞRU döndürür (YANLIŞ döndürmek için filtre-> bayrak = 1 ayarlamanız gerekir).

Filtrelere baktığımızda her şey yerli yerine oturdu. CP1251, KOI8-R'den ayırt edilemez. ISO-8859-5, kodlamalar listesindeyse, her zaman doğru olarak algılanacaktır.

Genel olarak, başarısız olun. Bu anlaşılabilir bir durumdur - yalnızca karakter kodlarıyla, bu kodlar farklı kodlamalarda kesiştiğinden, genel durumda kodlamayı bulmak imkansızdır.

2. Google'ın verdiği şeyler

Ve Google her türlü pisliği dağıtıyor. Kaynakları buraya bile yazmayacağım, isterseniz kendiniz bir bakın (http://'den sonraki boşluğu kaldırın, linksiz metni nasıl göstereceğimi bilmiyorum):

Http: // deer.org.ua/2009/10/06/1/
http://php.su/forum/topic.php?forum=1&topic=1346

3. Habr ile ara

1) tekrar karakter kodları:

2) bence çok ilginç bir çözüm:
Bağlantıdaki yorumdaki eksiler ve artılar. Şahsen, bu çözümün yalnızca kodlama tespiti için gereksiz olduğunu düşünüyorum - çok güçlü çıkıyor. İçindeki kodlamanın belirlenmesi bir yan etkidir).

4. Aslında benim kararım

Fikir, önceki bölümdeki ikinci bağlantıya bakarken ortaya çıktı. Fikir şu: Büyük bir Rusça metin alıyoruz, farklı harflerin frekanslarını ölçüyoruz ve bu frekansları kodlamayı tespit etmek için kullanıyoruz. İleriye baktığımda, büyük ve küçük harflerle ilgili sorunlar olacağını hemen söyleyeceğim. Bu nedenle, hem büyük/küçük harfe duyarlı hem de büyük/küçük harfe duyarsız (ikinci durumda, aynı sıklıkta küçük harfe daha da büyük bir harf ekledim ve tüm büyük olanlar). Bu "spektra"da, frekansları 0,001'den küçük olan tüm harfler ve bir boşluk kesilir. İşte Savaş ve Barış'ı düzenledikten sonra elde ettiklerim:

Büyük/küçük harfe duyarlı "spektrum":
dizi ("o" => 0.095249209893009, "e" => 0.06836817536026, "a" => 0.067481298384992, "u" => 0.055995027400041, "n" => 0.052242744063325, .... "e" => 0.002252892226507, "H "=> 0.0021318391371162," P "=> 0.0018574762967903," f "=> 0.0015961610948418," B "=> 0.0014044332975731," O "=> 0.0013188987793209," A "=> 0.0012623590130186," K "=>" 0.001061180 =>79 ,)

Büyük/küçük harfe duyarsız:
dizi ("O" => 0.095249209893009, "o" => 0.095249209893009, "E" => 0.06836817536026, "e" => 0.06836817536026, "A" => 0.067481298384992, "a" => 0.067481298384992, "I" => 0.0559950274000 , "ve" => 0.055995027400041, .... "C" => 0.0029893589260344, "c" => 0.0029893589260344, "u" => 0.0024649163501406, "U" => 0.0024649163501406, "E" => 0.002252892226507, "e" => 0.002252892226507, "F" => 0.0015961610948418, "f" => 0.0015961610948418,)

Farklı kodlamalardaki spektrumlar (dizi anahtarları, karşılık gelen kodlamadaki karşılık gelen karakterlerin kodlarıdır):

Daha öte. Bilinmeyen bir kodlama metni alıyoruz, test edilen her kodlama için mevcut karakterin frekansını buluyoruz ve onu bu kodlamanın “derecelendirmesine” ekliyoruz. Daha yüksek derecelendirmeye sahip bir kodlama, büyük olasılıkla bir metin kodlamasıdır.

$ kodlamaları = dizi ("cp1251" => "specter_cp1251.php" gerektirir, "koi8r" => "specter_koi8r.php" gerektirir, "iso88595" => "specter_iso88595.php" gerektirir); $ enc_rates = dizi (); için ($ ben = 0; $ ben< len($str); ++$i) { foreach ($encodings as $encoding =>$ char_specter) ($ enc_rates [$ kodlaması] + = $ char_specter)]; )) var_dump ($ enc_rates);
Bu kodu kendi başınıza çalıştırmayı denemeyin bile - işe yaramaz. Bunu sözde kod olarak düşünebilirsiniz - Makaleyi dağıtmamak için ayrıntıları atladım. $ char_specter tam olarak pastebin tarafından başvurulan dizilerdir.

Sonuçlar
Tablonun satırları metin kodlamasıdır, sütunlar $ enc_rates dizisinin içeriğidir.

1) $ str = "Rusça metin";
0.441 | 0.020 | 0.085 | Windows-1251
0,049 | 0.441 | 0.166 | KOI8-R
0.133 | 0.092 | 0.441 | ISO-8859-5

Hepsi mükemmel. Gerçek kodlama zaten diğerlerinden 4 kat daha yüksek bir dereceye sahip - bu böyle kısa bir metin için. Daha uzun metinlerde oran yaklaşık olarak aynı olacaktır.


cp1251 | koi8r | iso88595 |
0.013 | 0.705 | 0,331 | Windows-1251
0,649 | 0.013 | 0,201 | KOI8-R
0,007 | 0,392 | 0.013 | ISO-8859-5

Hata! Tam yulaf lapası. Ve çünkü CP1251'deki büyük harfler genellikle KOI8-R'deki küçük harflere karşılık gelir. Ve küçük harfler de büyük harflerden çok daha sık kullanılır. Bu yüzden CP1251'de büyük harfli diziyi KOI8-R olarak tanımlıyoruz.
Büyük/küçük harfe duyarsız yapmaya çalışıyorum

1) $ str = "Rusça metin";
cp1251 | koi8r | iso88595 |
0,477 | 0,342 | 0.085 | Windows-1251
0,315 | 0,477 | 0,207 | KOI8-R
0,216 | 0,321 | 0,477 | ISO-8859-5

2) $ str = "SAPPY RUSYA METNİ";
cp1251 | koi8r | iso88595 |
1.074 | 0.705 | 0,465 | Windows-1251
0,649 | 1.074 | 0,201 | KOI8-R
0,331 | 0,392 | 1.074 | ISO-8859-5

Gördüğünüz gibi, doğru kodlama hem büyük/küçük harfe duyarlı "spektra" (dize az sayıda büyük harf içeriyorsa) hem de büyük/küçük harfe duyarlı olmayanlarla tutarlı bir şekilde öndedir. İkinci durumda, büyük/küçük harfe duyarsız olanlarda, lider elbette o kadar emin değildir, ancak küçük satırlarda bile oldukça kararlıdır. Harflerin ağırlıklarıyla da oynayabilirsiniz - örneğin, onları frekansa göre doğrusal olmayan hale getirin.

5. Sonuç

Konu UTF-8 ile çalışmayı kapsamıyor - burada karakter kodlarını almanın ve bir dizeyi karakterlere bölmenin biraz daha uzun/karmaşık olması dışında temel bir fark yoktur.
Bu fikirler elbette sadece Kiril kodlamalarına genişletilemez - tek soru karşılık gelen dillerin / kodlamaların "tayfında".

not Çok gerekli/ilginç olacaksa - tam olarak çalışan kütüphanenin ikinci bölümünü GitHub'da yayınlayacağım. Gönderideki verilerin böyle bir kütüphaneyi hızlı bir şekilde yazmak ve kendi ihtiyaçlarınızı karşılamak için oldukça yeterli olduğuna inansam da - Rus dili için "spektrum" düzenlenmiştir, gerekli tüm kodlamalara kolayca aktarılabilir.