risposta-alla-domanda-sullo-sviluppo-web-bd.com

Rileva la codifica e rende tutto UTF-8

Sto leggendo un sacco di testi da vari feed RSS e inserendoli nel mio database.

Naturalmente, ci sono diverse codifiche di caratteri usate nei feed, ad es. UTF-8 e ISO-8859-1.

Sfortunatamente, a volte ci sono problemi con le codifiche dei testi. Esempio:

  1. Il "ß" in "Fußball" dovrebbe apparire così nel mio database: "Ÿ". Se è un "Â", viene visualizzato correttamente.

  2. A volte, la "ß" in "Fußball" appare nel mio database: "ß". Quindi viene visualizzato in modo errato, ovviamente.

  3. In altri casi, "ß" viene salvato come "ß", quindi senza alcuna modifica. Quindi viene visualizzato anche in modo errato.

Cosa posso fare per evitare i casi 2 e 3?

Come posso rendere tutto la stessa codifica, preferibilmente UTF-8? Quando devo usare utf8_encode(), quando devo usare utf8_decode() (è chiaro qual è l'effetto ma quando devo usare le funzioni?) E quando non devo fare nulla con l'input?

Puoi aiutarmi e dirmi come rendere ogni cosa la stessa codifica? Forse con la funzione mb_detect_encoding()? Posso scrivere una funzione per questo? Quindi i miei problemi sono:

  1. Come scoprire che cosa codifica il testo usa?
  2. Come convertirlo in UTF-8 - qualunque sia la vecchia codifica?

Funzionerebbe una funzione come questa?

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

L'ho provato ma non funziona. Che cosa c'è che non va?

287
caw

Se si applica utf8_encode() a una stringa già UTF8, verrà restituito un output UTF8 alterato.

Ho creato una funzione che risolve tutti questi problemi. Si chiama Encoding::toUTF8().

Non è necessario sapere qual è la codifica delle stringhe. Può essere Latin1 (iso 8859-1), Windows-1252 o UTF8, oppure la stringa può avere un mix di essi. Encoding::toUTF8() convertirà tutto in UTF8.

L'ho fatto perché un servizio mi stava dando un feed di dati tutti incasinati, mescolando UTF8 e Latin1 nella stessa stringa.

Uso:

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

Scaricare:

https://github.com/neitanod/forceutf8

Aggiornare:

Ho incluso un'altra funzione, Encoding::fixUFT8(), che risolverà ogni stringa UTF8 che sembra incomprensibile.

Uso:

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

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

Esempi:

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

produrrà:

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

Aggiornamento: ho trasformato la funzione (forceUTF8) in una famiglia di funzioni statiche su una classe chiamata Encoding. La nuova funzione è Encoding::toUTF8().

341

Per prima cosa devi rilevare quale codifica è stata utilizzata. Mentre stai analizzando i feed RSS (probabilmente via HTTP), dovresti leggere la codifica dal parametro charset del campo Content-Type HTTP . Se non è presente, leggi la codifica dall'attributo encoding dell'istruzione di elaborazione XML . Se anche questo è mancante, usa UTF-8 come definito nelle specifiche .


Modifica Ecco cosa probabilmente farei:

Vorrei usare cURL per inviare e recuperare la risposta. Ciò consente di impostare specifici campi di intestazione e di recuperare anche l'intestazione di risposta. Dopo aver recuperato la risposta, devi analizzare la risposta HTTP e dividerla in intestazione e corpo. L'intestazione dovrebbe quindi contenere il campo dell'intestazione Content-Type che contiene il tipo MIME e (si spera) il parametro charset con anche la codifica/il set di caratteri. In caso contrario, analizzeremo il PI XML per la presenza dell'attributo encoding e otterremo la codifica da lì. Se anche questo manca, le specifiche XML definiscono di utilizzare UTF-8 come codifica.

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

Rilevare la codifica è difficile.

mb_detect_encoding funziona indovinando, in base a un numero di candidati che si passa. In alcune codifiche alcune sequenze di byte non sono valide, pertanto può distinguere tra vari candidati. Sfortunatamente, ci sono molte codifiche, dove gli stessi byte sono validi (ma diversi). In questi casi, non c'è modo di determinare la codifica; È possibile implementare la propria logica per fare ipotesi in questi casi. Ad esempio, i dati provenienti da un sito giapponese potrebbero avere una maggiore probabilità di avere una codifica giapponese.

Finché si tratta solo di lingue dell'Europa occidentale, le tre principali codifiche da considerare sono utf-8, iso-8859-1 e cp-1252. Dal momento che queste sono le impostazioni predefinite per molte piattaforme, sono anche le più probabilità che vengano segnalate erroneamente. Per esempio. se le persone usano codifiche diverse, è probabile che siano sincere su questo, poiché altrimenti il ​​loro software si romperebbe molto spesso. Pertanto, una buona strategia è affidarsi al fornitore, a meno che la codifica non sia riportata come una di queste tre. Dovresti ancora confermare che è effettivamente valido, usando mb_check_encoding (nota che valido non è lo stesso di being - lo stesso input può essere valido per molte codifiche). Se è uno di questi, puoi usare mb_detect_encoding per distinguerli. Fortunatamente questo è abbastanza deterministico; Hai solo bisogno di usare la giusta sequenza di rilevamento, che è UTF-8,ISO-8859-1,WINDOWS-1252.

Una volta che hai rilevato la codifica, devi convertirla nella tua rappresentazione interna (UTF-8 è l'unica scelta sensata). La funzione utf8_encode trasforma ISO-8859-1 in UTF-8, quindi può essere utilizzata solo per quel particolare tipo di input. Per altre codifiche, usa mb_convert_encoding.

35
troelskn

A veramente Un modo piacevole per implementare una funzione isUTF8- può essere trovato su php.net :

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

Questo cheatsheet elenca alcuni avvertimenti comuni relativi alla gestione UTF-8 in PHP: http: //developer.loftdigital. com/blog/php-utf-8-bigino

Potrebbe rivelarsi utile anche questa funzione che rileva i caratteri multibyte in una stringa ( source ):


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

Un po 'di testa in alto, hai detto che il "ß" dovrebbe essere visualizzato come "Â" nel tuo database.

Questo è probabilmente dovuto al fatto che stai usando un database con la codifica di caratteri latin1 o forse la tua connessione php-mysql è impostata in modo errato, php crede che il tuo mysql sia impostato per usare utf-8, quindi invia i dati come utf8, ma il tuo mysql belives php sta inviando dati codificati come iso-8859-1, quindi potrebbe ancora una volta provare a codificare i dati inviati come utf-8, causando questo tipo di problemi.

Dai un'occhiata a questo, può aiutarti: http://php.net/manual/en/function.mysql-set-charset.php

9
Krynble

È necessario testare il set di caratteri sull'input poiché le risposte possono venire codificate con codifiche diverse.
Impongo che tutti i contenuti siano stati inviati in UTF-8 effettuando il rilevamento e la traduzione utilizzando la seguente funzione:

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

Questa routine trasformerà tutte le variabili PHP che provengono dall'host remoto in UTF-8.
Oppure ignora il valore se la codifica non può essere rilevata o convertita.
Puoi personalizzarlo in base alle tue esigenze.
Basta invocarlo prima di utilizzare le variabili.

3
cavila

La cosa interessante di mb_detect_encoding e mb_convert_encoding è che l'ordine delle codifiche da te suggerite è importante:

// $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)

Quindi potresti voler usare un ordine specifico quando specifichi le codifiche attese. Tuttavia, tieni presente che questo non è infallibile.

3
Halil Özgür

La codifica sembra codificata in UTF-8 due volte ; cioè, da qualche altra codifica, in UTF-8 e di nuovo in UTF-8. Come se avessi iso-8859-1, convertito da iso-8859-1 in utf-8, e trattato la nuova stringa come iso-8859-1 per un'altra conversione in UTF-8.

Ecco alcuni pseudocodice di ciò che hai fatto:

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

Dovresti provare:

  1. rileva la codifica usando mb_detect_encoding() o qualunque cosa tu voglia usare
  2. se è UTF-8, convertire in iso-8859-1 e ripetere il passaggio 1
  3. infine, riconvertire in UTF-8

Questo presumendo che nella conversione "intermedia" hai usato iso-8859-1. Se hai usato windows-1252, quindi converti in windows-1252 (latin1). La codifica sorgente originale non è importante; quello che hai usato in difetto, la seconda conversione è.

Questa è la mia ipotesi su quello che è successo; non c'è molto altro che si possa fare per ottenere quattro byte al posto di un byte esteso ASCII.

La lingua tedesca utilizza anche iso-8859-2 e windows-1250 (latin2).

3
Ivan Vučica

Elaborare la codifica dei caratteri dei feed RSS sembra essere complicato . Anche le normali pagine web spesso omettono, o mentono, la loro codifica.

Quindi potresti provare a utilizzare il modo corretto per rilevare la codifica e quindi ricorrere a qualche forma di rilevamento automatico (ipotesi).

2
Kevin ORourke

So che questa è una domanda più vecchia, ma immagino che una risposta utile non faccia mai male. Stavo avendo problemi con la mia codifica tra un'applicazione desktop, SQLite e variabili GET/POST. Alcuni sarebbero in UTF-8, alcuni sarebbero in ASCII, e in pratica tutto si farebbe fregare quando saranno coinvolti personaggi stranieri.

Ecco la mia soluzione. Scrub il tuo GET/POST/REQUEST (ho omesso i cookie, ma potresti aggiungerli se lo desideri) su ogni pagina caricata prima dell'elaborazione. Funziona bene in un colpo di testa. PHP genererà avvisi se non è in grado di rilevare automaticamente la codifica sorgente, quindi questi avvisi vengono soppressi con @.

//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){}
2
jocull

Stavo cercando soluzioni per la codifica da AGES, e questa pagina è probabilmente la conclusione di anni di ricerca! Ho provato alcuni dei suggerimenti che hai citato e qui sono i miei appunti:

Questa è la mia stringa di prova:

questa è una stringa "wròng wrìtten", ma non ho voglia di pù 'sòme' chàrs speciali per vederli, convertiti da fùnctìon !! & questo è tutto!

Faccio un INSERT per salvare questa stringa su un DB in un campo impostato come utf8_general_ci

Il set di caratteri della mia pagina è UTF-8

Se faccio un INSERT proprio così, nel mio DB ho probabilmente dei caratteri provenienti da Marte ... quindi ho bisogno di convertirli in qualche UTF-8 "sano". Ho provato utf8_encode() ma ancora i caratteri alieni stavano invadendo il mio database ...

Così ho provato ad usare la funzione forceUTF8 pubblicata sul numero 8 ma su DB la stringa salvata ha un aspetto simile:

questa è una stringa "wròng wrìtten", ma ho bisogno di vedere le loro parole speciali per vederle, convertite da fùnctìon !! & questo è tutto!

Quindi raccogliendo qualche informazione in più su questa pagina e unendoli ad altre informazioni su altre pagine ho risolto il mio problema con questa soluzione:

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

Ora nel mio database ho la mia stringa con la codifica corretta.

NOTA: L'unica nota a cui badare è la funzione mysql_client_encoding! Devi essere connesso al DB perché questa funzione vuole un ID risorsa come parametro.

Ma bene, faccio solo la ricodifica prima del mio INSERT, quindi per me non è un problema.

Spero che questo possa aiutare qualcuno come questa pagina mi ha aiutato!

Grazie a tutti!

Mauro

2
Mauro

È semplice: quando ottieni qualcosa che non è UTF8, devi codificare ENTO in utf8.

Quindi, quando stai recuperando un determinato feed che è ISO-8859-1, lo analizza tramite utf8_encode.

Tuttavia, se stai recuperando un feed UTF8, non devi fare nulla.

2
Seb

php.net/ mb_detect_encoding

echo mb_detect_encoding($str, "auto");

o

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

non so davvero quali sono i risultati, ma ti suggerisco di prendere alcuni dei tuoi feed con diverse codifiche e provare se mb_detect_encoding funziona o meno.

aggiornamento
auto è l'abbreviazione di "ASCII, JIS, UTF-8, EUC-JP, SJIS". restituisce il set di caratteri rilevato, che è possibile utilizzare per convertire la stringa in utf-8 con iconv .

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

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

non l'ho testato, quindi nessuna garanzia. e forse c'è un modo più semplice.

1
stefs

@harpax che ha funzionato per me. Nel mio caso, questo è abbastanza buono:

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

Ÿ è Mojibake per ß. Nel tuo database, potresti avere un esagono

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

Dovresti non usare le funzioni di codifica/decodifica in PHP; al contrario, è necessario impostare correttamente il database e la connessione.

Se MySQL è coinvolto, vedi: Problemi con utf8 caratteri; quello che vedo non è quello che ho memorizzato

0
Rick James

Dopo aver risolto gli script php, non dimenticare di dire a mysql quale set di caratteri stai passando e vorrebbe ricetrare.

Esempio: imposta il set di caratteri utf8

Passare i dati utf8 in una tabella latin1 in una sessione di I/O latin1 dà quei cattivi avicoli. Lo vedo ogni altro giorno nei negozi di oscommerce. Indietro e quarto potrebbe sembrare giusto. Ma phpmyadmin mostrerà la verità. Dicendo a mysql quale charset stai passando gestirà la conversione dei dati mysql per te.

Come recuperare i dati mysql criptati esistenti è un altro argomento da discutere. :)

0
tim

Ottieni la codifica dalle intestazioni e convertila in utf-8.

$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
Arsen

La risposta più votata non funziona. Ecco la mia e spero che aiuti.

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

Trovo la soluzione qui 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;
}

Penso che @ sia una cattiva decisione, e apporti alcune modifiche alla soluzione da deer.org.ua;

0
Paul