Kodlamayı tespit edin ve her şeyi UTF-8 yapın


304

Çeşitli RSS beslemelerinden çok sayıda metin okuyorum ve bunları veritabanıma ekliyorum.

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

Ne yazık ki, bazen metinlerin kodlanmasında sorunlar vardır. Misal:

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

  2. Bazen, "Fußball" daki "ß" veritabanımda şöyle görünüyor: "ß". Sonra yanlış görüntülenir.

  3. Diğer durumlarda, "ß" bir "ß" olarak kaydedilir - bu yüzden herhangi bir değişiklik yapılmaz. Sonra da yanlış görüntülenir.

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

Her şeyi nasıl aynı kodlamayı, tercihen UTF-8 yapabilirim? Ne zaman kullanmalıyım utf8_encode(), ne zaman kullanmalıyım utf8_decode()(etkinin ne olduğu açıktır, ancak işlevleri ne zaman kullanmalıyım?) Ve girişle ne zaman hiçbir şey yapmam gerekir?

Her şeyi aynı kodlamayı nasıl yapabilirim? Belki de fonksiyonu ile mb_detect_encoding()? Bunun için bir işlev yazabilir miyim? Yani benim sorunlarım:

  1. Metnin hangi kodlamasını kullandığını nasıl öğrenebilirim?
  2. Eski kodlama ne olursa olsun onu UTF-8'e nasıl dönüştürebilirim?

Böyle bir işlev işe yarar mı?

function correct_encoding($text) {
    $current_encoding = mb_detect_encoding($text, 'auto');
    $text = iconv($current_encoding, 'UTF-8', $text);
    return $text;
}

Test ettim, ama işe yaramıyor. Bunun nesi var?


36
"Fußball" daki "ß", veritabanımda şöyle görünmelidir: "Ÿ". ". Hayır ß gibi görünmeli. Harmanlama ve bağlantınızın doğru ayarlandığından emin olun. Aksi takdirde sıralama ve arama sizin için bozulur.
Zengin Bradshaw

5
Veritabanınız yanlış ayarlanmış. Unicode içeriği depolamak istiyorsanız, bunun için yapılandırın. Bu nedenle, PHP kodunuzdaki sorunu geçici olarak çözmek yerine, önce veritabanını düzeltmelisiniz.
dolmen

2
KULLANIM: $ from = mb_detect_encoding ($ text); $ metin = mb_convert_encoding ($ metin, 'UTF-8' dan $);
Informate.it

Yanıtlar:


363

utf8_encode()Zaten UTF-8 dizesine başvurursanız , bozuk UTF-8 çıktısı döndürür.

Tüm bu sorunları ele alan bir işlev yaptım. Buna denir Encoding::toUTF8().

Dizelerinizin kodlamasının ne olduğunu bilmenize gerek yoktur. Latin1 ( ISO 8859-1) , Windows-1252 veya UTF-8 olabilir veya dize bunların bir karışımına sahip olabilir. Encoding::toUTF8()her şeyi UTF-8'e dönüştürecek.

Bunu yaptım çünkü bir hizmet bana tüm veriyi dağıtıyordu, aynı dizede UTF-8 ve Latin1'i karıştırıyordu.

Kullanımı:

require_once('Encoding.php');
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::toUTF8($utf8_or_latin1_or_mixed_string);

$latin1_string = Encoding::toLatin1($utf8_or_latin1_or_mixed_string);

İndir:

https://github.com/neitanod/forceutf8

Encoding::fixUFT8()Bozuk görünen her UTF-8 dizesini düzelten başka bir işlev ekledim.

Kullanımı:

require_once('Encoding.php');
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::fixUTF8($garbled_utf8_string);

Örnekler:

echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");

çıktı olacak:

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

Function ( forceUTF8) işlevini bir sınıftaki statik işlevler ailesine dönüştürdüm Encoding. Yeni işlev Encoding::toUTF8().


1
Eğer koda bakarsanız, fixUTF8, string değişmeden geri dönene kadar forceUTF8'i tekrar tekrar çağırır. FixUTF8 () öğesine yapılan bir çağrı, forceUTF8 () öğesine yapılan çağrının en az iki katını alır, bu nedenle çok daha az performans gösterir. Ben sadece "kodlama bozuk" dosyaları düzeltir bir komut satırı programı oluşturmak için fixUTF8 () yaptım, ancak canlı bir ortamda nadiren gereklidir.
Sebastián Grignoli

3
Bu, geçersiz karakterlerin hangi kodlama ile başladığını bilmeden UTF8 dışı karakterleri UTF8'e nasıl dönüştürür?
philfreo

4
Cevap ISO-8859-1 varsayıyor, cevap zaten bunu söylüyor. ForceUTF8 () ve utf8_encode () arasındaki tek fark forceUTF8 () 'in UTF8 karakterlerini tanıması ve onları değiştirmemesidir.
Sebastián Grignoli

28
"Dizelerinizin kodlamasının ne olduğunu bilmenize gerek yok." - Çok katılmıyorum. Tahmin etmek ve denemek işe yarayabilir, ancak her zaman er ya da geç işe yaramadığı durumlarda karşılaşırsınız.
deceze

4
Tamamen katılıyorum. Aslında, genel bir kural olarak, kendinizi bulabileceğiniz durum buysa, bu sınıfın size yardımcı olabileceğini açıklamak istemedim.
Sebastián Grignoli

74

Önce hangi kodlamanın kullanıldığını tespit etmeniz gerekir. RSS beslemelerini (muhtemelen HTTP aracılığıyla) ayrıştırırken, kodlamayı HTTP üstbilgisi alanınıncharset parametresinden okumalısınız . Mevcut değilse , XML işleme talimatının niteliğinden kodlamayı okuyun . Bu da eksikse , spesifikasyonda tanımlandığı gibi UTF-8 kullanın .Content-Typeencoding


Düzenle    İşte muhtemelen ne yapacağım:

Bana kalırsa doğru cURL gönderip yanıt almak için. Bu, belirli başlık alanları ayarlamanıza ve yanıt başlığını almanıza olanak tanır. Yanıtı getirdikten sonra HTTP yanıtını ayrıştırıp üstbilgiye ve gövdeye ayırmanız gerekir. Başlık daha sonra Content-TypeMIME türünü içeren başlık alanını ve (umarız) charsetkodlama / karakter kümesiyle parametreyi içermelidir . Değilse, XML PI'yi encodingözelliğin varlığı açısından analiz edip kodlamayı oradan alacağız. Bu da eksikse, XML özellikleri UTF-8'i kodlama olarak kullanmayı tanımlar.

$url = 'http://www.lr-online.de/storage/rss/rss/sport.xml';

$accept = array(
    'type' => array('application/rss+xml', 'application/xml', 'application/rdf+xml', 'text/xml'),
    'charset' => array_diff(mb_list_encodings(), array('pass', 'auto', 'wchar', 'byte2be', 'byte2le', 'byte4be', 'byte4le', 'BASE64', 'UUENCODE', 'HTML-ENTITIES', 'Quoted-Printable', '7bit', '8bit'))
);
$header = array(
    'Accept: '.implode(', ', $accept['type']),
    'Accept-Charset: '.implode(', ', $accept['charset']),
);
$encoding = null;
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
$response = curl_exec($curl);
if (!$response) {
    // error fetching the response
} else {
    $offset = strpos($response, "\r\n\r\n");
    $header = substr($response, 0, $offset);
    if (!$header || !preg_match('/^Content-Type:\s+([^;]+)(?:;\s*charset=(.*))?/im', $header, $match)) {
        // error parsing the response
    } else {
        if (!in_array(strtolower($match[1]), array_map('strtolower', $accept['type']))) {
            // type not accepted
        }
        $encoding = trim($match[2], '"\'');
    }
    if (!$encoding) {
        $body = substr($response, $offset + 4);
        if (preg_match('/^<\?xml\s+version=(?:"[^"]*"|\'[^\']*\')\s+encoding=("[^"]*"|\'[^\']*\')/s', $body, $match)) {
            $encoding = trim($match[1], '"\'');
        }
    }
    if (!$encoding) {
        $encoding = 'utf-8';
    } else {
        if (!in_array($encoding, array_map('strtolower', $accept['charset']))) {
            // encoding not accepted
        }
        if ($encoding != 'utf-8') {
            $body = mb_convert_encoding($body, 'utf-8', $encoding);
        }
    }
    $simpleXML = simplexml_load_string($body, null, LIBXML_NOERROR);
    if (!$simpleXML) {
        // parse error
    } else {
        echo $simpleXML->asXML();
    }
}

Teşekkürler. Bu kolay olurdu. Ama gerçekten işe yarar mı? HTTP üstbilgilerinde veya XML özniteliklerinde genellikle yanlış kodlamalar verilir.
15'te caw

25
Tekrar: Bu senin sorunun değil. Bu tür sıkıntılardan kaçınmak için standartlar oluşturulmuştur. Eğer diğerleri onları takip etmezse, bu onların problemi, sizin değil.
Gumbo

Tamam, sanırım sonunda beni şimdi ikna ettin. :)
pençe

Kod için teşekkürler. Ama neden sadece bunu kullanmıyorsunuz? paste.bradleygill.com/index.php?paste_id=9651 Kodunuz çok daha karmaşık, bundan daha iyi ne olabilir?
gak

Öncelikle, biri HTTP başlığı ve diğeri veriler için olmak üzere iki istekte bulunuyorsunuz. İkincisi, herhangi bir görünümü için aradığınız charset=ve encoding=sadece uygun konumlarda ve. Üçüncüsü, beyan edilen kodlamanın kabul edilip edilmediğini kontrol etmiyorsunuz.
Gumbo

39

Kodlamanın tespiti zordur.

mb_detect_encodingtahmin ettiğinizde, onu geçtiğiniz birkaç adayı temel alarak çalışır. Bazı kodlamalarda, belirli bayt dizileri geçersizdir, bu nedenle çeşitli adaylar arasında ayrım yapabilir. Ne yazık ki, aynı baytların geçerli olduğu (ancak farklı) birçok kodlama vardır. Bu durumlarda, kodlamayı belirlemenin bir yolu yoktur; Bu durumlarda tahmin yapmak için kendi mantığınızı uygulayabilirsiniz. Örneğin, bir Japon sitesinden gelen verilerin Japonca kodlaması daha olasıdır.

Sürece sadece Batı Avrupa dilleri ile anlaşma olarak, üç büyük kodlamalar dikkate vardır için utf-8, iso-8859-1ve cp-1252. Bunlar birçok platform için varsayılan olduğundan, yanlış bir şekilde bildirilmesi en olası olanlardır. Örneğin. insanlar farklı kodlamalar kullanırlarsa, muhtemelen açıktırlar, çünkü yazılımları çok sık bozulur. Bu nedenle, kodlama bu üç yöntemden biri olarak rapor edilmedikçe, sağlayıcıya güvenmektir. Hala o kullanarak, gerçekten geçerli olduğunu doublecheck gerektiğini mb_check_encoding(O notu geçerli aynı şey değildir varlık - Aynı giriş birçok kodlanması için geçerli olabilir). Eğer onlardan biri ise, o zaman kullanabilirsinizmb_detect_encodingaralarında ayrım yapmak. Neyse ki bu oldukça belirleyicidir; Sadece uygun algılama sırasını kullanmanız gerekir UTF-8,ISO-8859-1,WINDOWS-1252.

Kodlamayı tespit ettikten sonra, onu dahili temsilciliğinize dönüştürmeniz gerekir ( UTF-8tek aklı başında seçimdir). Fonksiyon utf8_encodedönüşümleri ISO-8859-1için UTF-8, bu yüzden, sadece bu özel giriş tipine kullanılabilir. Diğer kodlamalar için kullanın mb_convert_encoding.


Çok teşekkür ederim! Daha iyisi: mb-convert-encoding () veya iconv ()? Farkların ne olduğunu bilmiyorum. Evet, yalnızca Batı Avrupa dillerini, özellikle İngilizce, Almanca ve Fransızca'yı ayrıştırmam gerekecek.
gak

7
Ben sadece gördüm: mb-tespit-kodlama () ist işe yaramaz. Yalnızca UTF-8, UTF-7, ASCII, EUC-JP, SJIS, eucJP-win, SJIS-win, JIS ve ISO-2022-JP'yi destekler. Benim için en önemlisi ISO-8859-1 ve WINDOWS-1252 desteklenmiyor. Bu yüzden mb-detek-encoding () kullanamıyorum.
gak

1
Benim, haklısın. Kullandığımdan beri bir süre geçti. Daha sonra kendi algılama kodunuzu yazmanız veya harici bir yardımcı program kullanmanız gerekir. UTF-8 oldukça güvenilir bir şekilde belirlenebilir, çünkü kaçış dizileri oldukça karakteristiktir. wp-1252 ve iso-8859-1 ayırt edilebilir, çünkü wp-1252 iso-8859-1'de yasa dışı olan baytlar içerebilir. Ayrıntıları almak için Wikipedia'yı kullanın ya da karakterle ilgili çeşitli işlevler altında php.net'in yorum bölümüne bakın.
troelskn

Özel şarkıların ortaya çıktığı biçimlere baktığınızda farklı kodlamaları ayırt edebileceğinizi düşünüyorum: Almanca "ß" farklı şekillerde ortaya çıkıyor: Bazen "Ÿ", bazen "Ã" ve bazen "ß". Neden?
gak

Evet, ama sonra dizeyi karşılaştırmadan önce içeriğini bilmeniz gerekir ve bu tür ilk etapta amacı yener. Almanca ß farklı kodlamalarda farklı değerlere sahip olduğu için farklı görünür. Somce karakterleri, farklı kodlamalarda aynı şekilde temsil edilir (örneğin, ascii karakter kümesindeki tüm karakterler, kullandığınız sürece, utf-8, iso-8859- * ve wp-1252'de aynı şekilde kodlanır) sadece bu karakterler, hepsi aynı görünüyor. Bu yüzden bazen ascii uyumlu olarak adlandırılırlar.
troelskn

14

Bir gerçekten bir uygulamaya güzel bir yol isUTF8-Fonksiyon bulunabilir php.net :

function isUTF8($string) {
    return (utf8_encode(utf8_decode($string)) == $string);
}

16
Ne yazık ki, bu yalnızca dize yalnızca ISO-8859-1'e dahil olan karakterlerden oluştuğunda çalışır. Ancak bu işe yarayabilir: @iconv ('utf-8', 'utf-8 // IGNORE', $ str) == $ str
Christian Davén

@Christian: Gerçekten de, Yüksek Performanslı MySQL yazarları da bunu tavsiye ediyor.
Alix Axel

1
Düzgün çalışmıyor: echo (int) isUTF8 ('z'); # 1 eko (int) isUTF8 (NULL); 1.
Yousha Aleayoub

1
Mükemmel olmasa da, bunun kabataslak bir UTF-8 kontrolü yapmanın güzel bir yolu olduğunu düşünüyorum.
Mateng

1
mb_check_encoding($string, 'UTF-8')
deceze

13

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

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


function detectUTF8($string)
{
    return preg_match('%(?:
        [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
        |\xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
        |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
        |\xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
        |\xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
        |[\xF1-\xF3][\x80-\xBF]{3}         # planes 4-15
        |\xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
        )+%xs', 
    $string);
}


2
Bence bu düzgün çalışmaz: echo detectUTF8 ('3٣3'); 1.
Yousha Aleayoub

10

Biraz kafa yukarı. Veritabanınızda "ß" ifadesinin "Ÿ" olarak görüntülenmesi gerektiğini söylediniz.

Bunun nedeni, Latin-1 karakter kodlaması olan bir veritabanı kullandığınız veya muhtemelen PHP-MySQL bağlantınızın yanlış ayarlandığı için, P'nin MySQL'inizin UTF-8 kullanacak şekilde ayarlandığına inanmasıdır, bu nedenle verileri UTF-8 olarak gönderir , ancak MySQL'iniz PHP'nin ISO 8859-1 olarak kodlanmış verileri gönderdiğine inanıyor, bu yüzden gönderilen verilerinizi bir kez daha UTF-8 olarak kodlamaya çalışabilir ve bu tür bir soruna neden olabilir.

Mysql_set_charset adresine bir göz atın . Size yardımcı olabilir.


4

Kodlamanız UTF-8'e iki kez kodlanmış gibi görünür ; başka bir kodlamadan UTF-8'e ve yine UTF-8'e. Sanki ISO 8859-1'e sahipmişsiniz, ISO 8859-1'den UTF-8'e dönüştürülmüş ve yeni dizeyi UTF-8'e başka bir dönüşüm için ISO 8859-1 olarak işlemiştir.

Yaptıklarınızın bazı sahte kodları:

$inputstring = getFromUser();
$utf8string = iconv($current_encoding, 'utf-8', $inputstring);
$flawedstring = iconv($current_encoding, 'utf-8', $utf8string);

Denemelisin:

  1. kullanarak mb_detect_encoding()veya ne kullanmak isterseniz onu kodlayın
  2. UTF-8 ise, ISO 8859-1'e dönüştürün ve 1. adımı tekrarlayın
  3. Son olarak, UTF-8'e dönüş

Bu, "orta" dönüşümde ISO 8859-1 kullandığınızı varsayar. Windows-1252 kullandıysanız, Windows-1252'ye (latin1) dönüştürün. Orijinal kaynak kodlaması önemli değildir; kusurlu olarak kullandığınız ikinci dönüşümdür.

Bu benim tahminime göre; bir genişletilmiş ASCII baytı yerine dört bayt almak için yapabileceğiniz çok az şey var.

Almanca da ISO 8859-2 ve Windows-1250 (Latin-2) kullanıyor.


3

İlginç olan mb_detect_encodingve mb_convert_encodingönerdiğiniz kodlamaların sırası meselesi yapmasıdır:

// $input is actually UTF-8

mb_detect_encoding($input, "UTF-8", "ISO-8859-9, UTF-8");
// ISO-8859-9 (WRONG!)

mb_detect_encoding($input, "UTF-8", "UTF-8, ISO-8859-9");
// UTF-8 (OK)

Bu nedenle, beklenen kodlamaları belirtirken belirli bir sipariş kullanmak isteyebilirsiniz. Yine de, bunun kusursuz olmadığını unutmayın.


2
Bu, ISO-8859-9'un pratikte herhangi bir ikili girişi kabul etmesi nedeniyle olur. Aynı şey Windows-1252 ve arkadaşları için de geçerli. İlk olarak, girişi kabul edemeyen kodlamaları test etmeniz gerekir.
Mikko Rantalainen

@MikkoRantalainen, evet, sanırım belgelerin bu kısmı benzer bir şey söylüyor: php.net/manual/en/function.mb-detect-order.php#example-2985
Halil Özgür

WHATWG HTML spesifikasyonunun Windows 1252'yi varsayılan kodlama olarak tanımladığı düşünüldüğünde, varsayılması oldukça güvenli olmalıdır if ($input_is_not_UTF8) $input_is_windows1252 = true;. Ayrıca bakınız: html.spec.whatwg.org/multipage/…
Mikko

3

Yanıtlar farklı kodlamalarla kodlanabileceğinden, girişteki karakter setini test etmeniz gerekir.

Aşağıdaki işlevi kullanarak algılama ve çeviri yaparak tüm içeriğin UTF-8'e gönderilmesini zorlarım:

function fixRequestCharset()
{
  $ref = array(&$_GET, &$_POST, &$_REQUEST);
  foreach ($ref as &$var)
  {
    foreach ($var as $key => $val)
    {
      $encoding = mb_detect_encoding($var[$key], mb_detect_order(), true);
      if (!$encoding)
        continue;
      if (strcasecmp($encoding, 'UTF-8') != 0)
      {
        $encoding = iconv($encoding, 'UTF-8', $var[$key]);
        if ($encoding === false)
          continue;
        $var[$key] = $encoding;
      }
    }
  }
}

Bu yordam, uzak ana bilgisayardan gelen tüm PHP değişkenlerini UTF-8'e dönüştürür.

Veya kodlama algılanamazsa veya dönüştürülemezse değeri yok sayın.

Gereksinimlerinize göre özelleştirebilirsiniz.

Değişkenleri kullanmadan önce çağırmanız yeterlidir.


kodlama listesinde başarılı olmadan mb_detect_order () kullanmanın amacı nedir?
giorgio79

Amaç, kullanılan php.ini dosyasında tanımlanan sistem tarafından yapılandırılmış sıralı kodlama dizisini döndürmektir. Bu, üçüncü parametreyi doldurmak için mb_detect_encoding tarafından gereklidir.
cavila

2

RSS yayınlarının karakter kodlamasını çözmek karmaşık görünmektedir . Normal web sayfaları bile kodlamalarını sıklıkla atlar veya hakkında yalan söyler.

Böylece, kodlamayı algılamak için doğru yolu kullanmayı deneyebilir ve ardından bir tür otomatik algılamaya (tahmin) geri dönebilirsiniz.


Kodlamayı feed bilgisinden okumak istemiyorum. Dolayısıyla, feed bilgilerinin yanlış olması eşittir. Kodlamayı metinden tespit etmek istiyorum.
gak

@ marco92w: Bildirilen kodlama yanlışsa sorun sizin değil. Eğlence için standartlar belirlenmemiştir.
Gumbo

1
@ Gumbo: ancak gerçek dünyada çalışıyorsanız, yanlış beyan edilen kodlamalar gibi şeylerle başa çıkabilmeniz gerekir. Sorun, kodlamayı sadece bir metinden tahmin etmenin (doğru) çok zor olmasıdır. Standartlar harika, ancak sayfaların / beslemelerin çoğu (en çok?) Bunlara uymuyor.
Kevin ORourke

@Kevin ORourke: Kesinlikle, doğru. Bu benim sorunum. @ Gumbo: Evet, bu benim sorunum. Yayınları okumak ve toplamak istiyorum. Bu yüzden yanlış kodlamaları düzeltmeliyim.
15'te caw

@ marco92w: Doğru kodlamayı ve geçerli kodlamayı bilmiyorsanız kodlamayı düzeltemezsiniz. Ve eğer charset/ encodingdeclaration ise: verilerin kodlandığı kodlamayı açıklayın.
Gumbo

2

Bunun daha eski bir soru olduğunu biliyorum, ama yararlı bir cevabın asla acıtmayacağını düşünüyorum. Bir masaüstü uygulaması, SQLite ve GET / POST değişkenleri arasındaki kodlamamla ilgili sorunlar yaşıyordum. Bazıları UTF-8'de, bazıları ASCII'de olacak ve yabancı karakterler dahil edildiğinde temelde her şey berbat olacaktı.

İşte benim çözümüm. İşlemeden önce GET / POST / REQUEST (çerezleri atladım, ancak istenirse ekleyebilirsiniz) ekler. Bir başlıkta iyi çalışır. PHP kaynak kodlamasını otomatik olarak algılayamazsa uyarılar atar, bu nedenle bu uyarılar @ 'larla bastırılır.

//Convert everything in our vars to UTF-8 for playing nice with the database...
//Use some auto detection here to help us not double-encode...
//Suppress possible warnings with @'s for when encoding cannot be detected
try
{
    $process = array(&$_GET, &$_POST, &$_REQUEST);
    while (list($key, $val) = each($process)) {
        foreach ($val as $k => $v) {
            unset($process[$key][$k]);
            if (is_array($v)) {
                $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = $v;
                $process[] = &$process[$key][@mb_convert_encoding($k,'UTF-8','auto')];
            } else {
                $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = @mb_convert_encoding($v,'UTF-8','auto');
            }
        }
    }
    unset($process);
}
catch(Exception $ex){}

Cevabınız için teşekkürler, jocull. Mb_convert_encoding () işlevi, burada zaten sahip olduğumuz şey, değil mi? ;) Yani cevabınızdaki tek yeni şey, tüm değişkenlerde kodlamayı değiştiren döngüler.
caw

2

Kodlama çözümlerine çağlardan beri bakıyordum ve bu sayfa muhtemelen yıllarca süren bir araştırmanın sonucu! Bahsettiğiniz bazı önerileri test ettim ve işte notlarım:

Bu benim test dizem:

Bu bir "wròng wrìtten" dize ama ben görmek için p to 'sòme' özel chàrs için nèed, fùnctìon tarafından convertèd !! & bu kadar!

Ben bu dize olarak ayarlanmış bir alanda bir veritabanına kaydetmek için bir INSERT yapmak utf8_general_ci

Sayfamın karakter kümesi UTF-8.

Eğer böyle bir INSERT yaparsam, veritabanımda muhtemelen Mars'tan gelen bazı karakterlerim var ...

Bu yüzden onları bir "aklı başında" UTF-8'e dönüştürmem gerekiyor. denedimutf8_encode() , ama yine de uzaylılar benim veritabanı işgal edildi ...

Bu yüzden forceUTF88 numaraya yayınlanan işlevi kullanmaya çalıştım , ancak veritabanında kaydedilen dize şöyle görünüyor:

Bu bir "wròngìtten" dize bà ¹t à me à me à à me à ch à ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch ch convert convert convert convert convert convert convert convert convert !! & bu kadar!

Bu sayfa hakkında daha fazla bilgi toplamak ve bunları diğer sayfalardaki diğer bilgilerle birleştirmek sorunumu bu çözümle çözdüm:

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

Şimdi benim veritabanı doğru kodlama ile benim dize var.

NOT: Dikkat edilmesi gereken sadece işlevdedir mysql_client_encoding! Veritabanına bağlı olmanız gerekir, çünkü bu işlev parametre olarak bir kaynak kimliği ister.

Ama iyi, sadece INSERT'imden önce yeniden kodlamayı yapıyorum, bu yüzden benim için sorun değil.


1
Neden sadece UTF-8mysql için istemci kodlamasını kullanmıyorsunuz ? Bu şekilde manuel dönüşüme ihtiyaç
duymazsınız

2

O kadar basit: UTF-8 olmayan bir şeyi olsun, sen gerekir kodlamak o içine UTF-8.

Yani, ISO 8859-1 olan belirli bir feed'i alırken ayrıştırın utf8_encode.

Ancak, UTF-8 özet akışı alıyorsanız hiçbir şey yapmanıza gerek yoktur.


Teşekkürler! Tamam, beslemenin nasıl kodlandığını mb-tespit-kodlaması () kullanarak öğrenebilirim, değil mi? Ancak feed ASCII ise ne yapabilirim? utf8-encode () sadece UTF-8'den ISO-8859-1'e kadar, değil mi?
gak

ASCII, ISO-8859-1 VE UTF-8'in bir alt kümesidir, bu nedenle utf8-encode () kullanmak bir değişiklik yapmamalıdır - EĞER sadece ASCII ise
Michael Borgwardt

UTF-8 değilse her zaman utf8_encode kullanabilir miyim? Bu gerçekten kolay olurdu. Mb-tespit-kodlamasına () göre ASCII olan metin "& # 228;" içeriyordu. Bu bir ASCII karakteri mi? Yoksa HTML mi?
gak

Bu HTML. Aslında bu kodlanmış, böylece belirli bir sayfaya yazdırdığınızda tamam görünüyor. İsterseniz önce ut8_encode () sonra html_entity_decode () yapabilirsiniz.
Seb

1
Ss karakteri UTC-8'de bayt dizisi 0xC39F ile kodlanır. Windows-1252 ile yorumlandığında, bu dizi characters (0xC3) ve Ÿ (0x9F) karakterlerini temsil eder. Ve bu bayt dizisini UTF-8 ile tekrar kodlarsanız, Windows-1252'de represents'yi temsil eden 0xC383 0xC29F alırsınız. Yani hatanız bu UTF-8 kodlu verileri UTF-8 dışında bir kodlamaya sahip bir şey olarak ele almaktır. Bu bayt dizisinin gördüğünüz karakter olarak sunulması sadece bir yorum meselesidir. Başka bir kodlama / karakter kümesi kullanırsanız, muhtemelen başka karakterler görürsünüz.
Gumbo

1

php.net/mb_detect_encoding

echo mb_detect_encoding($str, "auto");

veya

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

gerçekten sonuçların ne olduğunu bilmiyorum, ama sadece farklı kodlamalarla beslemelerinizin bazılarını almanızı ve mb_detect_encodingişe yarayıp yaramadığını deneyin .

güncelleme
otomatik "ASCII, JIS, UTF-8, EUC-JP, SJIS" kısaltmasıdır. dizeyi iconv ile utf-8'e dönüştürmek için kullanabileceğiniz algılanan karakter kümesini döndürür .

<?php
function convertToUTF8($str) {
    $enc = mb_detect_encoding($str);

    if ($enc && $enc != 'UTF-8') {
        return iconv($enc, 'UTF-8', $str);
    } else {
        return $str;
    }
}
?>

test etmedim, bu yüzden garanti yok. ve belki daha basit bir yol var.


Teşekkür ederim. İkinci argüman olarak 'auto' ve 'UTF-8, ASCII, ISO-8859-1' arasındaki fark nedir? 'Otomatik' daha fazla kodlamaya sahip mi? O zaman 'otomatik' kullanmak daha iyi olurdu, değil mi? Gerçekten herhangi bir hata olmadan çalışırsa o zaman sadece "ASCII" veya "ISO-8859-1" "UTF-8" değiştirmek gerekir. Nasıl?
gak

2
İşleviniz her durumda iyi çalışmıyor. Bazen bir hata alıyorum: Uyarı: iconv (): Giriş dizesinde geçersiz bir karakter algılandı ...
caw

1

@ harpax benim için çalıştı. Benim durumumda, bu yeterince iyi:

if (isUTF8($str)) { 
    echo $str; 
}
else
{
    echo iconv("ISO-8859-1", "UTF-8//TRANSLIT", $str);
}

0

Php betiklerini sıraladıktan sonra, mysql'e hangi karakterden geçtiğinizi ve almak istediğinizi söylemeyi unutmayın.

Örnek: set karakter kümesi utf8

Utf8 verilerini bir latin1 I / O oturumunda bir latin1 tablosuna geçirmek bu kötü kuş ayaklarını verir. Bunu her geçen gün oscommerce mağazalarında görüyorum. Geri ve dördüncü doğru görünebilir. Ancak phpmyadmin gerçeği gösterecektir. Mysql'e hangi karakter kümesini geçtiğinizi söyleyerek sizin için mysql verilerinin dönüştürülmesini yönetir.

Mevcut şifreli mysql verileri kurtarmak için nasıl tartışmak başka bir iş parçacığıdır. :)


0

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

class CharsetDetector
{
private static $CHARSETS = array(
"ISO_8859-1",
"ISO_8859-15",
"CP850"
);
private static $TESTCHARS = array(
"€",
"ä",
"Ä",
"ö",
"Ö",
"ü",
"Ü",
"ß"
);
public static function convert($string)
{
    return self::__iconv($string, self::getCharset($string));
}
public static function getCharset($string)
{
    $normalized = self::__normalize($string);
    if(!strlen($normalized))return "UTF-8";
    $best = "UTF-8";
    $charcountbest = 0;
    foreach (self::$CHARSETS as $charset) {
        $str = self::__iconv($normalized, $charset);
        $charcount = 0;
        $stop   = mb_strlen( $str, "UTF-8");

        for( $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;
            $best=$charset;
        }
        //echo $text."<br />";
    }
    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) $bytes = 4;
        elseif ($c > 223) $bytes = 3;
        elseif ($c > 191) $bytes = 2;
        else $ret .=$str[$i];
        if (($i + $bytes) > $len) $ret .=$str[$i];
        $ret2=$str[$i];
        while ($bytes > 1) {
            $i++;
            $b = ord($str[$i]);
            if ($b < 128 || $b > 191) {$ret .=$ret2; $ret2=""; $i+=$bytes-1;$bytes=1; break;}
            else $ret2.=$str[$i];
            $bytes--;
        }
    }
}
return $ret; 
}
private static function __iconv($string, $charset)
{
    return iconv ( $charset, "UTF-8" , $string );
}
}


0

Başlıklardan kodlama alın ve utf-8'e dönüştürün.

$post_url='http://website.domain';

/// Get headers ////////////////////////////////////////////////////////////
function get_headers_curl($url) 
{ 
    $ch = curl_init(); 

    curl_setopt($ch, CURLOPT_URL,            $url); 
    curl_setopt($ch, CURLOPT_HEADER,         true); 
    curl_setopt($ch, CURLOPT_NOBODY,         true); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_TIMEOUT,        15); 

    $r = curl_exec($ch); 
    return $r; 
}
$the_header = get_headers_curl($post_url);
/// check for redirect /////////////////////////////////////////////////
if (preg_match("/Location:/i", $the_header)) {
    $arr = explode('Location:', $the_header);
    $location = $arr[1];

    $location=explode(chr(10), $location);
    $location = $location[0];

$the_header = get_headers_curl(trim($location));
}
/// Get charset /////////////////////////////////////////////////////////////////////
if (preg_match("/charset=/i", $the_header)) {
    $arr = explode('charset=', $the_header);
    $charset = $arr[1];

    $charset=explode(chr(10), $charset);
    $charset = $charset[0];
    }
///////////////////////////////////////////////////////////////////////////////
// echo $charset;

if($charset && $charset!='UTF-8') { $html = iconv($charset, "UTF-8", $html); }

0

ŸMojibake için ß. Veritabanınızda onaltılık olabilir

DF if the column is "latin1",
C39F if the column is utf8 -- OR -- it is latin1, but "double-encoded"
C383C5B8 if double-encoded into a utf8 column

Sen gerektiğini değil PHP'de fonksiyonlar deşifre / herhangi kodlamasını kullanır; bunun yerine, veritabanını ve ona olan bağlantıyı doğru şekilde ayarlamanız gerekir.

MySQL söz konusuysa, bakınız: utf8 karakterleriyle ilgili sorun; gördüğüm sakladığım şey değil


0

Burada çözüm buluyorum http://deer.org.ua/2009/10/06/1/

class Encoding
{
    /**
     * http://deer.org.ua/2009/10/06/1/
     * @param $string
     * @return null
     */
    public static function detect_encoding($string)
    {
        static $list = ['utf-8', 'windows-1251'];

        foreach ($list as $item) {
            try {
                $sample = iconv($item, $item, $string);
            } catch (\Exception $e) {
                continue;
            }
            if (md5($sample) == md5($string)) {
                return $item;
            }
        }
        return null;
    }
}

$content = file_get_contents($file['tmp_name']);
$encoding = Encoding::detect_encoding($content);
if ($encoding != 'utf-8') {
    $result = iconv($encoding, 'utf-8', $content);
} else {
    $result = $content;
}

Ben @ @ kötü bir karar olduğunu düşünüyorum ve deer.org.ua çözüm bazı değişiklikler yapmak;


0

En çok oy verilen cevap işe yaramıyor. İşte benim ve umarım yardımcı olur.

function toUTF8($raw) {
    try{
        return mb_convert_encoding($raw, "UTF-8", "auto"); 
    }catch(\Exception $e){
        return mb_convert_encoding($raw, "UTF-8", "GBK"); 
    }
}

1
Neden veya dosyalarınızın nasıl farklı olduğu hakkında bir fikriniz var mı? Sizin için hangi parçalar işe yaramadı? Örneğin: Büyük harfli Almanca karakterler doğru şekilde dönüştürülmedi. Meraklı, "GBK" nedir?
SherylHohman

-1

Japonca ve Korece gibi çoklu dilleri ele almaya çalıştığınızda sorun yaşayabilirsiniz. 'auto' parametresi ile mb_convert_encoding iyi çalışmıyor. Mb_detect_order ('ASCII, UTF-8, JIS, EUC-JP, SJIS, EUC-KR, UHC') ayarı, EUC- * 'yi yanlış algılayacağından yardımcı olmaz.

Giriş dizeleri HTML'den geldiği sürece, bir meta öğede 'charset' kullanması gerektiği sonucuna vardım. Geçersiz HTML'yi desteklediğinden Basit HTML DOM Ayrıştırıcı kullanıyorum .

Aşağıdaki snippet, başlık öğesini bir web sayfasından ayıklar. Sayfanın tamamını dönüştürmek istiyorsanız, bazı satırları kaldırmak isteyebilirsiniz.

<?php
require_once 'simple_html_dom.php';

echo convert_title_to_utf8(file_get_contents($argv[1])), PHP_EOL;

function convert_title_to_utf8($contents)
{
    $dom = str_get_html($contents);
    $title = $dom->find('title', 0);
    if (empty($title)) {
        return null;
    }
    $title = $title->plaintext;
    $metas = $dom->find('meta');
    $charset = 'auto';
    foreach ($metas as $meta) {
        if (!empty($meta->charset)) { // html5
            $charset = $meta->charset;
        } else if (preg_match('@charset=(.+)@', $meta->content, $match)) {
            $charset = $match[1];
        }
    }
    if (!in_array(strtolower($charset), array_map('strtolower', mb_list_encodings()))) {
        $charset = 'auto';
    }
    return mb_convert_encoding($title, 'UTF-8', $charset);
}

-1

PhpQuery ( UTF-8 yerine ISO-8859-1) ile aynı sorunu vardı ve bu kesmek bana yardımcı oldu:

$html = '<?xml version="1.0" encoding="UTF-8" ?>' . $html;

mb_internal_encoding('UTF-8'), phpQuery::newDocumentHTML($html, 'utf-8'), mbstring.internal_encodingVe diğer manipülasyonlar herhangi bir etkisi almadı.


-1

'Otomatik' olmadan deneyin

Yani:

mb_detect_encoding($text)

onun yerine:

mb_detect_encoding($text, 'auto')

Daha fazla bilgi burada bulunabilir: mb_detect_encoding

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.