Rileva la codifica e crea tutto UTF-8


304

Sto leggendo molti testi da vari feed RSS e li inserisco nel mio database.

Ovviamente, ci sono diverse codifiche di caratteri utilizzate nei feed, ad esempio UTF-8 e ISO 8859-1.

Sfortunatamente, a volte ci sono problemi con la codifica dei testi. Esempio:

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

  2. A volte, il "ß" in "Fußball" appare così 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 uguale alla stessa codifica, preferibilmente UTF-8? Quando devo usare utf8_encode(), quando devo usare utf8_decode()(è chiaro quale sia l'effetto ma quando devo usare le funzioni?) E quando non devo fare nulla con l'input?

Come faccio a rendere tutto uguale alla stessa codifica? Forse con la funzione mb_detect_encoding()? Posso scrivere una funzione per questo? Quindi i miei problemi sono:

  1. Come faccio a sapere quale codifica utilizza il testo?
  2. Come posso convertirlo in UTF-8, qualunque sia la vecchia codifica?

Una funzione come questa funzionerebbe?

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?


36
"Il" ß "in" Fußball "dovrebbe apparire così nel mio database:" Ÿ ".". No, dovrebbe sembrare ß. Assicurarsi che regole di confronto e connessione siano impostate correttamente. Altrimenti l'ordinamento e la ricerca saranno interrotti per te.
Rich Bradshaw

5
Il tuo database non è configurato correttamente. Se vuoi archiviare il contenuto Unicode, configuralo per quello. Quindi, invece di provare a risolvere il problema nel tuo codice PHP, dovresti prima risolvere il database.
dolmen,

2
USE: $ from = mb_detect_encoding ($ text); $ text = mb_convert_encoding ($ testo, 'UTF-8', $ da);
Informate.it

Risposte:


363

Se si applica utf8_encode()a una stringa già UTF-8, verrà restituito un output UTF-8 confonduto.

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

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

L'ho fatto perché un servizio mi stava dando un feed di dati tutti incasinati, mescolando UTF-8 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);

Scarica:

https://github.com/neitanod/forceutf8

Ho incluso un'altra funzione, Encoding::fixUFT8()che risolverà ogni stringa UTF-8 che sembra confusa.

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

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


1
Bene, se guardi il codice, fixUTF8 chiama semplicemente forceUTF8 una volta e ancora finché la stringa non viene restituita invariata. Una chiamata a fixUTF8 () richiede almeno il doppio del tempo di una chiamata a forceUTF8 (), quindi è molto meno performante. Ho creato fixUTF8 () solo per creare un programma da riga di comando che riparasse i file "codificati danneggiati", ma raramente in un ambiente live è necessario.
Sebastián Grignoli,

3
In che modo questo converte i caratteri non UTF8 in UTF8, senza sapere con quale codifica devono iniziare i caratteri non validi?
philfreo,

4
Presuppone ISO-8859-1, la risposta dice già questo. L'unica differenza tra forceUTF8 () e utf8_encode () è che forceUTF8 () riconosce i caratteri UTF8 e li mantiene invariati.
Sebastián Grignoli,

28
"Non devi sapere qual è la codifica delle tue stringhe." - Non sono molto d'accordo. Indovinare e provare può funzionare, ma prima o poi ti imbatterai sempre in casi limite in cui non funziona.
ingannare

4
Sono totalmente d'accordo. In realtà, non intendevo affermarlo come regola generale, spiega solo che questa lezione potrebbe aiutarti se questa è la situazione in cui ti trovi.
Sebastián Grignoli il

74

Devi prima rilevare quale codifica è stata utilizzata. Mentre analizzi i feed RSS (probabilmente tramite HTTP), dovresti leggere la codifica dal charsetparametro del Content-Typecampo dell'intestazione HTTP . Se non è presente, leggere la codifica dal encodingattributo della istruzione di elaborazione XML . Se manca anche quello, usa UTF-8 come definito nelle specifiche .


Modifica    Ecco cosa probabilmente farei:

Userei cURL per inviare a prendere la risposta. Ciò consente di impostare campi di intestazione specifici e di recuperare anche l'intestazione di risposta. Dopo aver recuperato la risposta, è necessario analizzare la risposta HTTP e dividerla in intestazione e corpo. L'intestazione dovrebbe quindi contenere il Content-Typecampo di intestazione che contiene il tipo MIME e (si spera) anche il charsetparametro con la codifica / set di caratteri. In caso contrario, analizzeremo il PI XML per la presenza encodingdell'attributo e otterremo la codifica da lì. Se manca anche quello, le specifiche XML definiscono di usare 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();
    }
}

Grazie. Sarebbe facile Ma funzionerebbe davvero? Spesso ci sono codifiche errate fornite nelle intestazioni HTTP o negli attributi di XML.
caw

25
Ancora: non è un tuo problema. Sono stati stabiliti standard per evitare tali problemi. Se gli altri non li seguono, è un problema loro, non tuo.
Gumbo,

Ok, penso che mi hai finalmente convinto ora. :)
Caw,

Grazie per il codice Ma perché non usarlo semplicemente? paste.bradleygill.com/index.php?paste_id=9651 Il tuo codice è molto più complesso, cosa c'è di meglio con esso?
Caw

Bene, in primo luogo stai facendo due richieste, una per l'intestazione HTTP e una per i dati. In secondo luogo, siete alla ricerca di ogni forma di charset=e encoding=, e non solo nelle posizioni appropriate. E in terzo luogo, non stai verificando se la codifica dichiarata è accettata.
Gumbo,

39

Rilevare la codifica è difficile.

mb_detect_encodingfunziona indovinando, basandosi su un numero di candidati che lo supera. In alcune codifiche, alcune sequenze di byte non sono valide, pertanto può distinguere tra vari candidati. Sfortunatamente, ci sono molte codifiche, in cui gli stessi byte sono validi (ma diversi). In questi casi, non è possibile determinare la codifica; Puoi implementare la tua logica per fare ipotesi in questi casi. Ad esempio, i dati provenienti da un sito giapponese potrebbero avere maggiori probabilità di avere una codifica giapponese.

Finché hai a che fare solo con le lingue dell'Europa occidentale, le tre principali codifiche da considerare sono utf-8, iso-8859-1e cp-1252. Poiché si tratta di valori predefiniti per molte piattaforme, è anche probabile che vengano segnalati erroneamente. Per esempio. se le persone usano codifiche diverse, è probabile che siano franche al riguardo, poiché altrimenti il ​​loro software si romperà molto spesso. Pertanto, una buona strategia è quella di fidarsi del provider, a meno che la codifica non sia indicata come una di quelle tre. Dovresti ancora ricontrollare che sia effettivamente valido, usando mb_check_encoding(nota che valido non è lo stesso di essere - lo stesso input può essere valido per molte codifiche). Se è uno di quelli, è quindi possibile utilizzaremb_detect_encodingper distinguere tra loro. Fortunatamente questo è abbastanza deterministico; Devi solo usare la sequenza di rilevazione corretta, che è UTF-8,ISO-8859-1,WINDOWS-1252.

Una volta rilevata la codifica, devi convertirla nella tua rappresentazione interna ( UTF-8è l'unica scelta sana). La funzione si utf8_encodetrasforma ISO-8859-1in UTF-8, quindi può essere utilizzata solo per quel particolare tipo di input. Per altre codifiche, utilizzare mb_convert_encoding.


Grazie mille! Cosa c'è di meglio: mb-convert-encoding () o iconv ()? Non so quali siano le differenze. Sì, dovrò solo analizzare le lingue dell'Europa occidentale, in particolare inglese, tedesco e francese.
Caw

7
Ho appena visto: mb-detect-encoding () è inutile. Supporta solo UTF-8, UTF-7, ASCII, EUC-JP, SJIS, eucJP-win, SJIS-win, JIS e ISO-2022-JP. I più importanti per me, ISO-8859-1 e WINDOWS-1252, non sono supportati. Quindi non posso usare mb-detect-encoding ().
Caw

1
Mio, hai ragione. È da un po 'che non lo uso. Dovrai quindi scrivere il tuo codice di rilevamento o utilizzare un'utilità esterna. UTF-8 può essere determinato in modo abbastanza affidabile, perché le sue sequenze di escape sono abbastanza caratteristiche. wp-1252 e iso-8859-1 possono essere distinti perché wp-1252 può contenere byte illegali in iso-8859-1. Usa Wikipedia per ottenere i dettagli, o cerca nella sezione commenti di php.net, sotto varie funzioni relative a charset.
troelskn,

Penso che tu possa distinguere le diverse codifiche quando guardi le forme in cui emergono i canti speciali: il "ß" tedesco emerge in diverse forme: a volte "Ÿ", a volte "ß" e talvolta "ß". Perché?
Caw

Sì, ma poi devi conoscere il contenuto della stringa prima di confrontarla, e quel tipo di sconfigge lo scopo in primo luogo. Il tedesco ß appare in modo diverso perché ha valori diversi in diverse codifiche. I personaggi somce sono rappresentati allo stesso modo in diverse codifiche (ad es. Tutti i caratteri nel set di caratteri ASCII sono codificati nello stesso modo in utf-8, iso-8859- * e wp-1252), finché si utilizza solo quei personaggi, sembrano tutti uguali. Ecco perché a volte sono chiamati compatibili con ASCII.
troelskn,

14

Un modo davvero carino per implementare una funzione isUTF8è disponibile su php.net :

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

16
Sfortunatamente, questo funziona solo quando la stringa è composta solo da caratteri inclusi in ISO-8859-1. Ma questo potrebbe funzionare: @iconv ('utf-8', 'utf-8 // IGNORE', $ str) == $ str
Christian Davén,

@Christian: In effetti, è quello che raccomandano anche gli autori di MySQL ad alte prestazioni.
Alix Axel,

1
Non funziona correttamente: echo (int) isUTF8 ('z'); # 1 echo (int) isUTF8 (NULL); # 1
Yousha Aleayoub,

1
Anche se non perfetto, penso che questo sia un buon modo per implementare un assurdo controllo UTF-8.
Mateng,

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

13

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

Questa funzione che rileva i caratteri multibyte in una stringa potrebbe anche rivelarsi utile ( fonte ):


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
Penso che non funzioni correttamente: echo detectUTF8 ('3٣3'); # 1
Yousha Aleayoub

10

Un po 'a testa alta. Hai detto che "ß" dovrebbe essere visualizzato come "Ÿ" nel tuo database.

Ciò è probabilmente dovuto al fatto che stai utilizzando un database con codifica dei caratteri Latin-1 o che la tua connessione PHP-MySQL è impostata in modo errato, ovvero P ritiene che MySQL sia impostato per utilizzare UTF-8, quindi invia i dati come UTF-8 , ma il tuo MySQL ritiene che PHP stia inviando dati codificati come ISO 8859-1, quindi potrebbe tentare ancora una volta di codificare i dati inviati come UTF-8, causando questo tipo di problemi.

Dai un'occhiata a mysql_set_charset . Potrebbe esserti d'aiuto.


4

La tua codifica sembra che tu abbia codificato due volte in UTF-8 ; 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 a UTF-8 e trattato la nuova stringa come ISO 8859-1 per un'altra conversione in UTF-8.

Ecco alcuni pseudocodici di ciò che hai fatto:

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

Dovresti provare:

  1. rilevare la codifica utilizzando mb_detect_encoding() o qualunque cosa tu voglia usare
  2. se è UTF-8, converti in ISO 8859-1 e ripeti il ​​passaggio 1
  3. infine, converti nuovamente in UTF-8

Ciò presume che nella conversione "centrale" hai usato ISO 8859-1. Se hai usato Windows-1252, convertilo in Windows-1252 (latino1). La codifica sorgente originale non è importante; quello che hai usato nella seconda conversione imperfetto è.

Questa è la mia ipotesi su ciò che è accaduto; c'è ben poco altro che avresti potuto fare per ottenere quattro byte al posto di un byte ASCII esteso.

La lingua tedesca utilizza anche ISO 8859-2 e Windows-1250 (latino-2).


3

La cosa interessante di mb_detect_encodinge mb_convert_encodingè che l'ordine delle codifiche che suggerisci è 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 previste. Tuttavia, tieni presente che questo non è infallibile.


2
Ciò accade perché ISO-8859-9 in pratica accetterà qualsiasi input binario. Lo stesso vale per Windows-1252 e gli amici. Devi prima testare le codifiche che non riescono ad accettare l'input.
Mikko Rantalainen,

@MikkoRantalainen, sì, immagino che questa parte della documentazione dica qualcosa di simile: php.net/manual/en/function.mb-detect-order.php#example-2985
Halil Özgür

Considerando che le specifiche HTML di WHATWG definiscono Windows 1252 come codifica predefinita, dovrebbe essere abbastanza sicuro da assumere if ($input_is_not_UTF8) $input_is_windows1252 = true;. Vedi anche: html.spec.whatwg.org/multipage/…
Mikko Rantalainen,

3

È necessario testare il set di caratteri sull'input poiché le risposte possono essere codificate con codifiche diverse.

Forzare tutto il contenuto inviato in UTF-8 eseguendo 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;
      }
    }
  }
}

Quella 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.

Richiamalo prima di usare le variabili.


qual è lo scopo dell'utilizzo di mb_detect_order () senza un elenco di codifica passato?
giorgio79,

Lo scopo è di restituire l'array ordinato di codifiche configurato dal sistema definito in php.ini utilizzato. Questo è richiesto da mb_detect_encoding per riempire il terzo parametro.
cavila,

2

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

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


Non voglio leggere la codifica dalle informazioni del feed. Quindi è uguale se le informazioni del feed sono errate. Vorrei rilevare la codifica dal testo.
Caw

@ marco92w: non è un problema se la codifica dichiarata è errata. Gli standard non sono stati stabiliti per divertimento.
Gumbo,

1
@Gumbo: ma se lavori nel mondo reale devi essere in grado di gestire cose come codifiche dichiarate errate. Il problema è che è molto difficile indovinare (correttamente) la codifica solo da un testo. Gli standard sono meravigliosi, ma molte (la maggior parte?) Delle pagine / dei feed non sono conformi.
Kevin ORourke,

@Kevin ORourke: Esatto, esatto. Questo è il mio problema @Gumbo: Sì, è un mio problema. Voglio leggere i feed e aggregarli. Quindi devo correggere le codifiche sbagliate.
caw

@ marco92w: Ma non puoi correggere la codifica se non conosci la codifica corretta e la codifica corrente. Ed è per questo che la dichiarazione charset/ encodingse: descrivi la codifica in cui sono codificati i dati.
Gumbo,

2

So che questa è una domanda più vecchia, ma immagino che una risposta utile non faccia mai male. Avevo problemi con la mia codifica tra un'applicazione desktop, SQLite e variabili GET / POST. Alcuni sarebbero in UTF-8, altri in ASCII, e praticamente tutto sarebbe incasinato quando fossero coinvolti personaggi stranieri.

Ecco la mia soluzione Scrub GET / POST / REQUEST (ho omesso i cookie, ma è possibile aggiungerli se lo si desidera) su ogni caricamento della pagina prima dell'elaborazione. Funziona bene in un'intestazione. PHP emetterà avvisi se non è in grado di rilevare automaticamente la codifica di origine, quindi questi avvisi vengono eliminati 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){}

Grazie per la risposta, jocull. La funzione mb_convert_encoding () è quella che abbiamo già avuto qui, giusto? ;) Quindi l'unica cosa nuova nella tua risposta sono i loop per cambiare la codifica in tutte le variabili.
Caw

2

Stavo cercando soluzioni per la codifica da secoli cercando , e questa pagina è probabilmente la conclusione di anni di ricerca! Ho testato alcuni dei suggerimenti che hai citato ed ecco i miei appunti:

Questa è la mia stringa di test:

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

Faccio un INSERT per salvare questa stringa su un database 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 database, ho alcuni personaggi probabilmente provenienti da Marte ...

Quindi ho bisogno di convertirli in alcuni "sani" UTF-8. Ci ho provato utf8_encode(), ma i caratteri alieni stavano invadendo il mio database ...

Quindi ho provato ad usare la funzione forceUTF8pubblicata sul numero 8, ma nel database la stringa salvata appare così:

questa è una stringa di "wrÃngng wrìtten" ma ho bisogno di chè 's'me' speciali per vederli, convertiti da fùnctÃonon !! & questo è tutto!

Quindi, raccogliendo alcune informazioni in più su questa pagina e unendole con 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: solo la nota di cui occuparsi è in funzione mysql_client_encoding! È necessario essere connessi al database, perché questa funzione richiede un ID risorsa come parametro.

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


1
Perché non usi solo la UTF-8codifica client per mysql? In questo modo non sarebbe necessaria la conversione manuale
Esailija

2

E 'semplice: quando si ottiene qualcosa che non è UTF-8, è necessario codificare che in UTF-8.

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

Tuttavia, se stai recuperando un feed UTF-8, non devi fare nulla.


Grazie! OK, posso scoprire come viene codificato il feed utilizzando mb-detect-encoding (), giusto? Ma cosa posso fare se il feed è ASCII? utf8-encode () è solo per ISO-8859-1 a UTF-8, non è vero?
caw

ASCII è un sottoinsieme di ISO-8859-1 E UTF-8, quindi l'utilizzo di utf8-encode () non dovrebbe apportare modifiche - SE in realtà è solo ASCII
Michael Borgwardt,

Quindi posso sempre usare utf8_encode se non è UTF-8? Questo sarebbe davvero facile. Il testo che era ASCII secondo mb-detect-encoding () conteneva "& # 228;". È un carattere ASCII? O è HTML?
caw

Questo è HTML. In realtà è codificato, quindi quando lo stampi in una determinata pagina mostra ok. Se lo desideri, puoi prima ut8_encode () quindi html_entity_decode ().
Seb,

1
Il carattere ß è codificato in UTF-8 con la sequenza di byte 0xC39F. Interpretata con Windows-1252, quella sequenza rappresenta i due caratteri  (0xC3) e Ÿ (0x9F). E se codifichi di nuovo questa sequenza di byte con UTF-8, otterrai 0xC383 0xC29F ciò che rappresenta ß in Windows-1252. Quindi il tuo errore è gestire questi dati codificati UTF-8 come qualcosa con una codifica diversa da UTF-8. Che questa sequenza di byte sia presentata come il personaggio che stai vedendo è solo una questione di interpretazione. Se usi un'altra codifica / set di caratteri, probabilmente vedrai altri caratteri.
Gumbo,

1

php.net/mb_detect_encoding

echo mb_detect_encoding($str, "auto");

o

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

davvero non so quali siano i risultati, ma suggerirei di prendere solo alcuni dei tuoi feed con codifiche diverse e provare se mb_detect_encodingfunziona o no.

update
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 provato, quindi nessuna garanzia. e forse c'è un modo più semplice.


Grazie. Qual è la differenza tra "auto" e "UTF-8, ASCII, ISO-8859-1" come secondo argomento? 'Auto' presenta più codifiche? Quindi sarebbe meglio usare "auto", no? Se funziona davvero senza alcun bug, devo solo cambiare "ASCII" o "ISO-8859-1" in "UTF-8". Come?
Caw

2
La tua funzione non funziona bene in tutti i casi. A volte ricevo un errore: Avviso: iconv (): rilevato un carattere non valido nella stringa di input in ...
caw

1

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

0

Dopo aver risolto i tuoi script php, non dimenticare di dire a mysql quale set di caratteri stai passando e vorresti ricevere.

Esempio: set di caratteri set utf8

Passando i dati utf8 a una tabella latin1 in una sessione I / O latin1 si ottengono quei brutti uccelli. Lo vedo a giorni alterni nei negozi di oscommerce. Indietro e quarto potrebbe sembrare giusto. Ma phpmyadmin mostrerà la verità. Dicendo a mysql quale set di caratteri stai passando gestirà la conversione dei dati mysql per te.

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


0

Questa versione è per la lingua tedesca ma è possibile modificare $ CHARSETS e $ 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 );
}
}


0

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

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

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

Si dovrebbe non utilizzare alcun codifica / decodifica funzioni in PHP; invece, è necessario impostare correttamente il database e la connessione ad esso.

Se è coinvolto MySQL, consultare: Problemi con i caratteri utf8; ciò che vedo non è ciò che ho archiviato


0

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 apporta alcune modifiche alla soluzione da deer.org.ua;


0

La risposta più votata non funziona. Ecco il mio 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"); 
    }
}

1
Hai qualche idea del perché o dei diversi file? Quali parti non hanno funzionato per te? Ad esempio: i caratteri tedeschi maiuscoli non sono stati convertiti correttamente. Curioso, cos'è "GBK"?
SherylHohman,

-1

Quando provi a gestire più lingue come giapponese e coreano potresti avere dei problemi. mb_convert_encoding con parametro 'auto' non funziona bene. L'impostazione di mb_detect_order ('ASCII, UTF-8, JIS, EUC-JP, SJIS, EUC-KR, UHC') non aiuta poiché rileverà EUC- * in modo errato.

Ho concluso che fintanto che le stringhe di input provengono da HTML, dovrebbe usare "charset" in un meta elemento. Uso Simple HTML DOM Parser perché supporta HTML non valido.

Lo snippet di seguito estrae l'elemento del titolo da una pagina Web. Se desideri convertire l'intera pagina, potresti voler rimuovere alcune righe.

<?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

Ho avuto lo stesso problema con phpQuery ( ISO-8859-1 invece di UTF-8 ) e questo trucco mi ha aiutato:

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

mb_internal_encoding('UTF-8'), phpQuery::newDocumentHTML($html, 'utf-8'), mbstring.internal_encodingE altre manipolazioni non ha preso alcun effetto.


-1

Prova senza "auto"

Questo è:

mb_detect_encoding($text)

invece di:

mb_detect_encoding($text, 'auto')

Ulteriori informazioni sono disponibili qui: mb_detect_encoding

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.