Kodlamasını algılar ve her şeyi UTF-8 yapmak

20 Cevap php

Çeşitli RSS beslemeleri metinlerin çok dışarı okuma ve benim veritabanına koymadan ediyorum.

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

Ne yazık ki, metinlerin kodlamaları ile bazen sorunlar vardır. Örnek:

1) "Fußball" in "ß" benim veritabanında bu gibi görünmelidir: "AY". Bir "ay" ise, doğru görüntülenir.

2) Bazen "Fußball" in "ß" benim veritabanında bu gibi görünüyor: "ß". Daha sonra tabii ki, yanlış gösterilir.

3) Diğer durumlarda, "ß" a "ß" olarak kaydedilir - yani herhangi bir değişiklik olmadan. Daha sonra, aynı zamanda yanlış gösterilir.

Ne Olgu 2 ve 3 önlemek için ne yapabilirim?

Nasıl her şeyi aynı kodlama, tercihen UTF-8 yapabilir? Ne zaman utf8_encode kullanmanız gerekir (), ben utf8_decode kullanmanız gerekir zaman () (bu etkinin ne olduğu belli ama ne zaman ben işlevleri kullanmak gerekir?) Ve ne zaman girdi ile bir şey yapmak gerekir?

Can you help me and tell me how to make everything the same encoding? Perhaps with the function mb-detect-encoding()? Can I write a function for this? So my problems are: 1) How to find out what encoding the text uses 2) How to convert it to UTF-8 - whatever the old encoding is

Şimdiden teşekkürler!

EDIT: Would a function like this work?

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

Bunu test ettik ama çalışmıyor. Bunun nesi yanlış?

20 Cevap

Eğer zaten UTF8 dize utf8_encode () uygularsanız o bozuk bir UTF8 çıktı dönecektir.

Tüm bu sorunları gideren bir işlev yaptı. Bu Kodlama :: toUTF8 () denir.

Sen dizeleri kodlama ne olduğunu bilmek gerekmez. Bu Latin1 (iso 8859-1), Windows 1252 veya UTF8 veya dize bunların bir karışımı olabilir olabilir. Kodlama :: toUTF8 () UTF8 için her şeyi dönüştürmek olacaktır.

Bir hizmet, aynı dize UTF8 ve Latin1 karıştırma, bana bütün berbat bir veri beslemesi veriyordu çünkü ben yaptım.

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

Güncelleme:

Ben başka bir işlevi, kodlama :: fixUFT8 () dahil ettik, wich bozuk görünüyor her UTF8 dizesi çözecektir.

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ısı:

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: I've transformed the function (forceUTF8) into a family of static functions on a class called Encoding. The new function is Encoding::toUTF8().

Önce kullanılan şey kodlama algılamak zorundayız. Eğer (muhtemelen HTTP üzerinden) RSS beslemeleri ayrıştırma konum olarak, Content-Type HTTP başlık alanının ve . If it is not present, read the encoding from the encoding vasfının charset parametresinden kodlama okumalısınız XML processing instruction. O da eksik olursa, use UTF-8 as defined in the specification.


Edit Burada muhtemelen yapacağını budur:

I cURL yanıtı göndermek ve almak için kullanmak istiyorum. Yani belirli bir başlık alanlarını ayarlamak ve de yanıt başlığını almak için izin verir. Yanıtını getiriliyor sonra HTTP yanıtı ayrıştırmak ve başlık ve gövde bölmek zorunda. Başlığı sonra MIME türünü ve kodlama / de charset ile (umarım) charset parametresini içeren Content-Type başlık alanını içermelidir. Değilse, encoding öznitelik varlığı için analiz XML PI ve oradan da kodlama elde edeceğiz. Bu da eksik ise, XML özellikleri kodlaması olarak UTF-8 kullanmak için tanımlayabilirsiniz.

$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();
    }
}

Kodlamasını tespit zordur.

mb_detect_encoding bunu geçmek aday sayısına göre, tahmin çalışır. Bazı kodlamalar, bazı bayt dizileri bir nedenle çeşitli adaylar arasında ayırt edebilirsiniz, geçersizdir. Ne yazık ki, aynı byte geçerli (ancak farklı) olan kodlamaları, bir yeri vardır. Bu durumlarda, kodlama belirlemek için bir yolu yoktur; Bu durumlarda tahmin yapmak için kendi mantığı uygulayabilirsiniz. Örneğin, bir Japon sitesinden gelen veriler Japon kodlama olması daha muhtemel olabilir.

Sürece sadece düşünün Avrupa Batı dillerinde, üç büyük kodlamaları ile uğraşmak gibi utf-8, iso-8859-1 ve cp-1252 vardır. Bu çok platformlar için varsayılan olduğundan, onlar hakkında da yanlış rapor edilecek muhtemeldir. Örn. insanlar farklı kodlamaları kullanırsanız kendi yazılım çok sık kıracak başka bir yana, onlar, bu konuda samimi olması muhtemeldir. Bu nedenle, iyi bir strateji kodlama bu üç biri olarak bildirilmiştir sürece, sağlayıcı güvenmek. Aynı giriş birçok kodlamaları için geçerli olabilir - Sen hala mb_check_encoding (yani valid being olarak aynı değildir unutmayın kullanarak, gerçekten geçerli olduğunu doublecheck gerekir .) O bu biriyse, o zaman mb_detect_encoding aralarında ayrım için kullanabilirsiniz. Neyse ki oldukça deterministik değildir; Sen sadece bir doğru algılamak-dizisi, kullanmanız gerekir UTF-8,ISO-8859-1,WINDOWS-1252.

Eğer iç gösterimi dönüştürmek gerekir kodlamasını tespit ettik (UTF-8 tek aklı başında bir seçimdir). Fonksiyonu utf8_encode dönüşümleri ISO-8859-1 UTF-8, bu nedenle, sadece belirli giriş türü için kullanılabilir. Diğer kodlamalar için mb_convert_encoding.

This cheatsheet lists some common caveats related to UTF-8 handling in PHP: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet

Bir dize çokbaytlı karakterleri tespit Bu fonksiyon aynı zamanda yararlı kanıtlamak (source) olabilir:


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);
}

Bir isUTF8-fonksiyonu uygulamak için bir really güzel bir yol bulunabilir php.net:

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

Biraz kafaları yukarı, "ß" veritabanındaki "Ay" olarak görüntülenir gerektiğini söyledi.

Eğer latin1 karakter kodlama ile bir veritabanı kullanarak konum ya da muhtemelen php-mysql bağlantısı yanlış ayarlanmış olduğundan bu muhtemelen sizin mysql Bu, php mysql utf-8 kullanmak için ayarlanır inanıyor, bu yüzden utf8 olarak veri gönderir, ama inanan, php iso-8859-1 olarak kodlanmış veri gönderirken, bu yüzden bir kez daha sorun bu tür neden utf-8 olarak gönderilen verileri kodlamak için deneyebilirsiniz.

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

Çok basit: Eğer UTF8 olmayan bir şey olsun, sen utf8 INTO olduğunu kodlamak gerekir.

Yani, utf8_encode aracılığıyla ISO-8859-1 ayrıştırma bu kadar belli bir beslemeyi çekerken zaman.

Bir UTF8 beslemeyi çekerken konum Ancak, bir şey yapmanız gerekmez.

RSS beslemeleri karakter kodlamasını çalışma dışarı görünüyor complicated. Hatta normal web sayfaları genellikle ihmal, ya da kodlama, hakkında yalan.

Yani kodlama algılamak ve daha sonra (tahmin) arka otomatik algılama çeşit düşmesine doğru şekilde kullanmayı deneyebilirsiniz.

UTF-8 twice içine kodlanmış gibi kodlama görünüyor; bu UTF-8 içine, diğer bazı kodlama, ve yine UTF-8 içine. Iso-8859-1 vardı sanki, utf-8 iso-8859-1 dönüştürülmüş ve UTF-8 başka bir dönüşüm için iso-8859-1 gibi yeni dize tedavi.

Burada ne yaptığını bazı pseudocode bulunuyor:

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

Sen denemelisiniz:

  1. Kullanmak istediğiniz ne olursa olsun mb_detect_encoding() kullanarak kodlamasını tespit veya
  2. UTF-8 ise, iso-8859-1 dönüştürmek ve 1. adımı tekrarlayın
  3. Sonunda, geri UTF-8 dönüştürmek

Bu "orta" dönüşüm iso-8859-1 kullanılan küstah edilir. Windows-1252 kullandıysanız, o zaman windows-1252 dönüştürmek (latin1). Orijinal kaynak kodlaması önemli değildir; Eğer kusurlu, ikinci dönüşümde kullanılan biridir.

Bu ne benim tahminim; Bir genişletilmiş ASCII bayt yerine dört bayt almak için yapmış olabilir başka çok az var.

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

Bu eski bir soru olduğunu biliyorum ama ben yararlı bir cevap acıyor asla rakam. Ben bir masaüstü uygulaması, SQLite arasında benim kodlama ile ilgili sorunlar yaşıyorsanız, ve / POST değişkenleri GET edildi. Bazı UTF-8 olacağını, bazı ASCII olacağını ve yabancı karakterler dahil aldığımda temelde her şey berbat olacaktı.

İşte benim çözümdür. Bu işlemeden önce her sayfada yükü (Ben kurabiye ihmal, ama istenirse bunları ekleyebilirsiniz) GET / POST / TALEP scrubs. Bu bir başlık iyi çalışır. PHP otomatik kaynak kodlamasını tespit edemez eğer uyarıları atmak olacaktır, bu yüzden bu uyarılar @ 's ile 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){}

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

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

Japon ve Kore gibi çoklu dil işlemek için çalıştığınızda belaya olabilir. 'auto' parametresi ile mb_convert_encoding iyi çalışmaz. Mb_detect_order Ayar ('ASCII, UTF-8, JIS-JP, SJIS, EUC-KR, UHC') yanlış bir şekilde, EUC-* algılar beri yardımcı olmuyor.

Ben sürece girdi dizeleri HTML olarak geliyor, bu bir meta öğesi 'charset' kullanmanız gerektiği sonucuna varmıştır. Geçersiz HTML destekler çünkü ben Simple HTML DOM Parser kullanın.

Aşağıdaki pasajı bir web sayfası başlık elemanı ayıklar. Eğer tüm sayfayı dönüştürmek istiyorsanız, o zaman 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);
}

mb_detect_encoding ve mb_convert_encoding ile ilgili ilginç bir şey önermek kodlamaları sipariş 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)

Yani beklenen kodlamaları belirtirken, belirli bir düzen kullanmak isteyebilirsiniz. Yine de, bu kusursuz olmadığını unutmayın.

Yukarıda değinildiği: kodlama sorunları oldukça sıkıcı olabilir.

I've used a guide on http://www.phpwact.org/php/i18n/charsets (with a link to a dedicated utf-8 guide), and this resolve my issues. The page is still under construction, but is does provide a very precise description of the relevant issues when using utf-8.

Case 3 aslında ne istediklerini gibi geliyor: karakterler veritabanında doğru. Genellikle dize görüntülemeden önce bir kez utf8_encode uygulamak yeterlidir.

I was checking for solutions to encoding since AGES, and this page is probably the conclusion of years of search! I tested some of the suggestions you mentioned and here's my notes:

Bu benim test dizedir:

this is a "wròng wrìtten" string bùt I nèed to pù 'sòme' special chàrs to see thèm, convertèd by fùnctìon!! & that's it!

Ben "utf8_general_ci" olarak ayarlanmış bir alanda DB Bu dize kaydetmek için kaydetmek

benim sayfa charset "UTF-8" dir

if I do an INSERT just like that, in my DB I have some chars probably coming from Mars... so I need to convert them into some "sane" UTF-8. I tried utf8_encode() but still aliens chars were invading my database...

bu yüzden "forceUTF8" gibi dize kaydedilmiş görünüyor 8 numara üzerinde ama DB yayınlanan işlevi kullanmak için çalıştı:

this is a "wròng wrìtten" string bùt I nèed to pù 'sòme' special chà rs to see thèm, convertèd by fùnctìon!! & that's it!

bu nedenle bu sayfada bazı daha çok bilgi toplama ve bu çözüm ile benim sorunum çözüldü diğer sayfalarda diğer Infos ile bunları birleştirerek:

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

şimdi benim veritabanında ben doğru kodlama ile benim dize var.

NOTE: Only note to take care of is on function mysql_client_encoding! you need to be connected to DB because this function wants a resource ID as parameter.

ama iyi, ben sadece yeniden kodlama benim INSERT önce bir sorun değil benim için yani bunu.

Ben bu sayfada gibi birisi bana yardımcı yardımcı olacağını umuyoruz!

herkese teşekkürler!

Mauro

Php komut sıralama sonra, geçen ne charset mysql anlatmak için unutmayın ve recceive istiyorum.

Örnek: set karakter seti utf8

Bir latin1 I / O oturumda bir latin1 tabloya utf8 verileri geçenler pis birdfeets verir. Ben oscommerce dükkanlarda her gün görüyoruz. Geri ve dördüncü doğru görünebilir. Ama phpmyadmin gerçeği gösterecektir. Eğer geçiyoruz ne charset mysql anlatarak sizin için mysql veri dönüşüm idare edecek.

Nasıl kurtarılır şifreli mysql veri mevcut görüşmek için başka bir iplik. :)

This version is for German language but you can modifiy the $CHARSETS and the $TESTCHARS

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 );
}
}

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

$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); }

I phpQuery (ISO-8859-1 yerine UTF-8) ile aynı sorunu vardı ve bu hack 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_encoding ve diğer manipülasyonlar herhangi bir etkisi olmadı.