UTF-8 fino in fondo

Nov 11 2008

Sto configurando un nuovo server e desidero supportare completamente UTF-8 nella mia applicazione web. L'ho provato in passato su server esistenti e mi sembra sempre di dover ricorrere a ISO-8859-1.

Dove devo impostare esattamente la codifica / i set di caratteri? Sono consapevole che devo configurare Apache, MySQL e PHP per farlo: c'è qualche lista di controllo standard che posso seguire, o forse risolvere i problemi dove si verificano le discrepanze?

Questo è per un nuovo server Linux, che esegue MySQL 5, PHP, 5 e Apache 2.

Risposte

1044 chazomaticus Nov 11 2008 at 04:43

Archiviazione dei dati :

  • Specificare il utf8mb4set di caratteri su tutte le tabelle e le colonne di testo nel database. Questo fa sì che MySQL archivi e recuperi fisicamente i valori codificati in modo nativo in UTF-8. Nota che MySQL utilizzerà implicitamente la utf8mb4codifica se utf8mb4_*viene specificato un confronto (senza alcun set di caratteri esplicito).

  • Nelle versioni precedenti di MySQL (<5.5.3), sarai sfortunatamente costretto a usare semplicemente utf8, che supporta solo un sottoinsieme di caratteri Unicode. Vorrei scherzare.

Accesso ai dati :

  • Nel codice dell'applicazione (ad esempio PHP), in qualsiasi metodo di accesso al database che utilizzi, dovrai impostare il set di caratteri di connessione su utf8mb4. In questo modo, MySQL non esegue alcuna conversione dal suo UTF-8 nativo quando trasferisce i dati all'applicazione e viceversa.

  • Alcuni driver forniscono il proprio meccanismo per configurare il set di caratteri di connessione, che aggiorna il proprio stato interno e informa MySQL della codifica da utilizzare sulla connessione: questo è di solito l'approccio preferito. In PHP:

    • Se stai utilizzando il livello di astrazione PDO con PHP ≥ 5.3.6, puoi specificare charsetnel DSN :

       $dbh = new PDO('mysql:charset=utf8mb4');
      
    • Se stai usando mysqli , puoi chiamare set_charset():

        $mysqli->set_charset('utf8mb4');       // object oriented style
        mysqli_set_charset($link, 'utf8mb4');  // procedural style
      
    • Se sei bloccato con il semplice mysql ma ti capita di eseguire PHP ≥ 5.2.3, puoi chiamare mysql_set_charset.

  • Se il driver non fornisce un proprio meccanismo per impostare il set di caratteri di connessione, potrebbe essere necessario emettere una query per dire a MySQL come l'applicazione si aspetta dati sul collegamento da codificare: SET NAMES 'utf8mb4'.

  • La stessa considerazione per quanto riguarda utf8mb4/ si utf8applica come sopra.

Uscita :

  • Se l'applicazione trasmette testo ad altri sistemi, anche questi dovranno essere informati della codifica dei caratteri. Con le applicazioni web, il browser deve essere informato della codifica in cui vengono inviati i dati (tramite intestazioni di risposta HTTP o metadati HTML ).

  • In PHP, puoi utilizzare l' default_charsetopzione php.ini o emettere manualmente l' Content-Typeintestazione MIME da solo, che è solo più lavoro ma ha lo stesso effetto.

  • Quando si codifica l'output utilizzando json_encode(), aggiungere JSON_UNESCAPED_UNICODEcome secondo parametro.

Ingresso :

  • Sfortunatamente, dovresti verificare che ogni stringa ricevuta sia UTF-8 valida prima di provare a memorizzarla o usarla ovunque. PHP mb_check_encoding()fa il trucco, ma devi usarlo religiosamente. Non c'è davvero alcun modo per aggirare questo, poiché i client dannosi possono inviare dati in qualsiasi codifica desiderino, e non ho trovato un trucco per convincere PHP a farlo per te in modo affidabile.

  • Dalla mia lettura delle attuali specifiche HTML , i seguenti sotto-punti non sono più necessari o addirittura validi per l'HTML moderno. La mia comprensione è che i browser funzioneranno e invieranno i dati nel set di caratteri specificato per il documento. Tuttavia, se scegli come target versioni precedenti di HTML (XHTML, HTML4 e così via), questi punti potrebbero comunque essere utili:

    • Solo per HTML prima di HTML5 : vuoi che tutti i dati che ti vengono inviati dai browser siano in UTF-8. Purtroppo, se si va in l'unico modo per farlo in modo affidabile questo è aggiungere l' accept-charsetattributo per tutti i tuoi <form>tag: <form ... accept-charset="UTF-8">.
    • Solo per HTML prima di HTML5 : nota che la specifica HTML del W3C dice che i client "dovrebbero" inviare automaticamente i moduli al server in qualsiasi set di caratteri servito dal server, ma questa è apparentemente solo una raccomandazione, da qui la necessità di essere esplicito su ogni singolo <form>etichetta.

Altre considerazioni sul codice :

  • Ovviamente, tutti i file che servirai (PHP, HTML, JavaScript, ecc.) Dovrebbero essere codificati in UTF-8 valido.

  • Devi assicurarti che ogni volta che elabori una stringa UTF-8, lo fai in modo sicuro. Questa è, sfortunatamente, la parte difficile. Probabilmente vorrai fare un ampio uso dell'estensione di PHP mbstring.

  • Le operazioni sulle stringhe incorporate in PHP non sono sicure per UTF-8. Ci sono alcune cose che puoi fare in sicurezza con le normali operazioni sulle stringhe PHP (come la concatenazione), ma per la maggior parte delle cose dovresti usare la mbstringfunzione equivalente .

  • Per sapere cosa stai facendo (leggi: non rovinare tutto), devi davvero conoscere UTF-8 e come funziona al livello più basso possibile. Dai un'occhiata a uno qualsiasi dei link da utf8.com per alcune buone risorse per imparare tutto ciò che devi sapere.

157 mercator Nov 13 2008 at 02:27

Vorrei aggiungere una cosa all'eccellente risposta di chazomaticus :

Non dimenticare nemmeno il tag META (come questo, o la versione HTML4 o XHTML ):

<meta charset="utf-8">

Sembra banale, ma IE7 mi ha dato problemi con quello prima.

Stavo facendo tutto bene; il database, la connessione al database e l'intestazione HTTP Content-Type erano tutti impostati su UTF-8 e funzionavano bene in tutti gli altri browser, ma Internet Explorer insisteva ancora per utilizzare la codifica "Europa occidentale".

Si è scoperto che nella pagina mancava il tag META. L'aggiunta di ciò ha risolto il problema.

Modificare:

Il W3C ha in realtà una sezione piuttosto ampia dedicata a I18N . Hanno una serie di articoli relativi a questo problema, che descrivono il lato HTTP, (X) HTML e CSS delle cose:

  • Domande frequenti: modifica della codifica della pagina HTML (X) in UTF-8
  • Dichiarazione di codifiche di caratteri in HTML
  • Tutorial: set di caratteri e codifiche in XHTML, HTML e CSS
  • Impostazione del parametro del set di caratteri HTTP

Raccomandano di utilizzare sia l'intestazione HTTP che il meta tag HTML (o la dichiarazione XML in caso di XHTML servito come XML).

66 chroder Nov 11 2008 at 04:30

Oltre a impostare default_charsetin php.ini, puoi inviare il set di caratteri corretto utilizzando header()dal tuo codice, prima di qualsiasi output:

header('Content-Type: text/html; charset=utf-8');

Lavorare con Unicode in PHP è facile fintanto che ti rendi conto che la maggior parte delle funzioni di stringa non funziona con Unicode e alcune potrebbero alterare completamente le stringhe . PHP considera i "caratteri" lunghi 1 byte. A volte va bene (ad esempio, explode()cerca solo una sequenza di byte e la usa come separatore, quindi non importa quali caratteri effettivi cerchi). Ma altre volte, quando la funzione è effettivamente progettata per funzionare sui caratteri , PHP non ha idea che il tuo testo abbia caratteri multibyte che si trovano con Unicode.

Una buona libreria in cui controllare è phputf8 . Questo riscrive tutte le funzioni "cattive" in modo da poter lavorare in sicurezza sulle stringhe UTF8. Ci sono estensioni come l'estensione mbstring che cercano di farlo anche per te, ma preferisco usare la libreria perché è più portabile (ma scrivo prodotti per il mercato di massa, quindi è importante per me). Ma phputf8 può usare mbstring dietro le quinte, comunque, per aumentare le prestazioni.

37 JimW. Sep 11 2012 at 22:40

Ho riscontrato un problema con qualcuno che utilizzava PDO e la risposta è stata di usarlo per la stringa di connessione PDO:

$pdo = new PDO(
    'mysql:host=mysql.example.com;dbname=example_db',
    "username",
    "password",
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

Il sito da cui l'ho preso è inattivo, ma fortunatamente sono stato in grado di ottenerlo utilizzando la cache di Google.

25 JDelage Feb 24 2012 at 05:20

Nel mio caso, stavo usando mb_split, che usa regex. Pertanto ho anche dovuto assicurarmi manualmente che la codifica regex fosse utf-8 in questo modomb_regex_encoding('UTF-8');

Come nota a margine, ho anche scoperto eseguendo mb_internal_encoding()che la codifica interna non era utf-8 e l'ho modificata eseguendo mb_internal_encoding("UTF-8");.

23 JimmyKane Jan 27 2014 at 16:16

Prima di tutto se sei in <5.3PHP, allora no. Hai un sacco di problemi da affrontare.

Sono sorpreso che nessuno abbia menzionato la libreria intl , quella che ha un buon supporto per unicode , grafemi , operazioni sulle stringhe , localizzazione e molti altri, vedi sotto.

Citerò alcune informazioni sul supporto Unicode in PHP dalle diapositive di Elizabeth Smith su PHPBenelux'14

INTL

Buono:

  • Wrapper intorno alla libreria ICU
  • Impostazioni locali standardizzate, impostazione delle impostazioni internazionali per script
  • Formattazione dei numeri
  • Formattazione della valuta
  • Formattazione del messaggio (sostituisce gettext)
  • Calendari, date, fuso orario e ora
  • Traslitteratore
  • Spoofchecker
  • Pacchetti di risorse
  • Convertitori
  • Supporto IDN
  • Graphemes
  • Fascicolazione
  • Iteratori

Male:

  • Non supporta zend_multibite
  • Non supporta la conversione dell'output di input HTTP
  • Non supporta il sovraccarico delle funzioni

mb_string

  • Abilita il supporto zend_multibyte
  • Supporta la codifica HTTP in / out trasparente
  • Fornisce alcuni wrapper per funzionalità come strtoupper

ICONV

  • Primario per la conversione del set di caratteri
  • Gestore del buffer di output
  • funzionalità di codifica MIME
  • conversione
  • alcuni aiutanti di stringa (len, substr, strpos, strrpos)
  • Filtro stream stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

BANCHE DATI

  • mysql: set di caratteri e regole di confronto sulle tabelle e sulla connessione (non le regole di confronto). Inoltre, non utilizzare mysql - msqli o PDO
  • postgresql: pg_set_client_encoding
  • sqlite (3): assicurati che sia stato compilato con il supporto unicode e intl

Alcuni altri trucchi

  • Non è possibile utilizzare nomi di file Unicode con PHP e Windows a meno che non si utilizzi un'estensione di terza parte.
  • Invia tutto in ASCII se stai usando exec, proc_open e altre chiamate da riga di comando
  • Il testo normale non è testo normale, i file hanno codifiche
  • Puoi convertire i file al volo con il filtro iconv

Aggiornerò questa risposta nel caso in cui le cose cambino funzionalità aggiunte e così via.

15 PuertoAGP Sep 10 2014 at 10:39

L'unica cosa che aggiungerei a queste incredibili risposte è sottolineare il salvataggio dei file nella codifica utf8, ho notato che i browser accettano questa proprietà rispetto all'impostazione di utf8 come codifica del codice. Qualsiasi editor di testo decente ti mostrerà questo, ad esempio Notepad ++ ha un'opzione di menu per la codifica dei file, ti mostra la codifica corrente e ti consente di cambiarla. Per tutti i miei file php utilizzo utf8 senza BOM.

Qualche tempo fa qualcuno mi ha chiesto di aggiungere il supporto utf8 per un'applicazione php / mysql progettata da qualcun altro, ho notato che tutti i file erano codificati in ANSI, quindi ho dovuto usare ICONV per convertire tutti i file, cambiare le tabelle del database per usare il utf8 charset e utf8_general_ci collate, aggiungi 'SET NAMES utf8' al livello di astrazione del database dopo la connessione (se usi 5.3.6 o precedente altrimenti devi usare charset = utf8 nella stringa di connessione) e cambia le funzioni della stringa per usare il multibyte php funzioni stringa equivalenti.

14 MiguelStevens Jan 13 2014 at 16:37

Recentemente ho scoperto che l'utilizzo strtolower()può causare problemi in cui i dati vengono troncati dopo un carattere speciale.

La soluzione era usare

mb_strtolower($string, 'UTF-8');

mb_ utilizza MultiByte. Supporta più caratteri ma in generale è un po 'più lento.

10 AbdulSadikYalcin May 06 2015 at 04:36

Ho appena affrontato lo stesso problema e ho trovato una buona soluzione nei manuali PHP.

Ho cambiato tutta la mia codifica dei file in UTF8, quindi la codifica predefinita sulla mia connessione. Questo ha risolto tutti i problemi.

if (!$mysqli->set_charset("utf8")) { printf("Error loading character set utf8: %s\n", $mysqli->error);
} else {
   printf("Current character set: %s\n", $mysqli->character_set_name());
}

Vedi la fonte

9 JW. Nov 11 2008 at 04:29

In PHP, dovrai utilizzare le funzioni multibyte o attivare mbstring.func_overload . In questo modo cose come strlen funzioneranno se hai caratteri che richiedono più di un byte.

Dovrai anche identificare il set di caratteri delle tue risposte. Puoi usare AddDefaultCharset, come sopra, o scrivere codice PHP che restituisca l'intestazione. (Oppure puoi aggiungere un tag META ai tuoi documenti HTML.)

7 jalf Nov 11 2008 at 04:48

Il supporto Unicode in PHP è ancora un enorme pasticcio. Sebbene sia in grado di convertire una stringa ISO8859 (che utilizza internamente) in utf8, non ha la capacità di lavorare con le stringhe Unicode in modo nativo, il che significa che tutte le funzioni di elaborazione delle stringhe manterranno e corromperanno le tue stringhe. Quindi è necessario utilizzare una libreria separata per il corretto supporto di utf8 o riscrivere da soli tutte le funzioni di gestione delle stringhe.

La parte facile è semplicemente specificare il set di caratteri nelle intestazioni HTTP e nel database e così via, ma niente di tutto ciò ha importanza se il tuo codice PHP non restituisce UTF8 valido. Questa è la parte difficile e PHP non ti dà praticamente alcun aiuto. (Penso che PHP6 dovrebbe risolvere il peggio di questo, ma è ancora un po 'di tempo)

7 BudimirGrom Feb 12 2015 at 06:52

Se vuoi che il server MySQL decida il set di caratteri, e non PHP come client (vecchio comportamento; preferito, secondo me), prova ad aggiungere skip-character-set-client-handshakeal tuo my.cnf, sotto [mysqld]e riavvia mysql.

Ciò potrebbe causare problemi nel caso in cui si utilizzi qualcosa di diverso da UTF8.

6 commonpike Jan 14 2011 at 23:13

La risposta migliore è eccellente. Ecco cosa ho dovuto fare su una normale configurazione debian / php / mysql:

// storage
// debian. apparently already utf-8

// retrieval
// the mysql database was stored in utf-8, 
// but apparently php was requesting iso. this worked: 
// ***notice "utf8", without dash, this is a mysql encoding***
mysql_set_charset('utf8');

// delivery
// php.ini did not have a default charset, 
// (it was commented out, shared host) and
// no http encoding was specified in the apache headers.
// this made apache send out a utf-8 header
// (and perhaps made php actually send out utf-8)
// ***notice "utf-8", with dash, this is a php encoding***
ini_set('default_charset','utf-8');

// submission
// this worked in all major browsers once apache
// was sending out the utf-8 header. i didnt add
// the accept-charset attribute.

// processing
// changed a few commands in php, like substr,
// to mb_substr

questo era tutto !

2 castro_pereira Mar 25 2019 at 02:27

se vuoi una soluzione mysql, ho avuto problemi simili con 2 dei miei progetti, dopo una migrazione del server. Dopo aver cercato e provato molte soluzioni, mi sono imbattuto in questo / niente prima che questo funzionasse):

mysqli_set_charset($con,"utf8");

Dopo aver aggiunto questa riga al mio file di configurazione, tutto funziona correttamente!

Ho trovato questa soluzione https://www.w3schools.com/PHP/func_mysqli_set_charset.asp quando stavo cercando di risolvere un inserto da una query html

in bocca al lupo!

Accountantم Aug 24 2019 at 02:10

Solo una nota:

Si trovano ad affrontare il problema dei vostri caratteri non latini sta mostrando come ?????????, lei ha chiesto una domanda, e ha ottenuto chiuso con un riferimento alla presente domanda canonica, si è tentato di tutto e non importa quello che fai è ancora ottenere ??????????da MySQL.

Ciò è principalmente dovuto al fatto che stai testando i tuoi vecchi dati che sono stati inseriti nel database utilizzando il set di caratteri sbagliato e sono stati convertiti e archiviati effettivamente nei caratteri del punto interrogativo ?. Il che significa che hai perso per sempre il tuo testo originale e qualunque cosa provi, otterrai ???????.

Applicare ciò che hai imparato dalle risposte a questa domanda su un nuovo dato potrebbe risolvere il tuo problema.

IjazAhmedBhatti Sep 27 2020 at 13:24

in connection.php: mysqli_set_charset ($ con, "utf8"); e nelle regole di confronto sql utf = 8