UTF-8 fino in fondo


1191

Sto configurando un nuovo server e desidero supportare pienamente UTF-8 nella mia applicazione web. Ho provato questo in passato su server esistenti e sembra sempre che debba ricadere su ISO-8859-1.

Dove devo esattamente impostare la codifica / i set di caratteri? Sono consapevole del fatto che devo configurare Apache, MySQL e PHP per fare ciò: esiste una lista di controllo standard che posso seguire o forse risolvere i problemi in cui si verificano le discrepanze?

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


8
Ecco una panoramica di tutti gli errori di codifica che puoi eventualmente fare: sebastianviereck.de/it/…
Sebastian Viereck,

13
Ecco un'introduzione alle codifiche in generale e alle codifiche in PHP in particolare: Ciò che ogni programmatore deve assolutamente e positivamente sapere sulle
inganno

Alcune recenti discussioni su PHP 7 indicano che non ci sono cambiamenti nella posizione "ufficialmente abbandonata" del 2010 ... C'è qualcosa in più su "PHP7 e UTF-8"?
Peter Krauss,

Questo problema è comune Ma non esiste una soluzione scorciatoia, dovrai configurarla utf-8per ognuna di esse separatamente - MySQL 5, PHP 5 O Apache 2.
Manish Shrivastava,

Risposte:


1016

Archiviazione dati :

  • Specificare il utf8mb4set di caratteri su tutte le tabelle e colonne di testo nel database. Questo fa sì che MySQL memorizzi e recuperi fisicamente i valori codificati nativamente 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), sfortunatamente sarai costretto a usare semplicemente utf8, che supporta solo un sottoinsieme di caratteri Unicode. Vorrei scherzare.

Accesso ai dati :

  • Nel codice dell'applicazione (ad es. PHP), in qualunque metodo di accesso al DB utilizzato, è necessario impostare il set di caratteri di connessione utf8mb4. In questo modo, MySQL non esegue alcuna conversione dal suo UTF-8 nativo quando trasmette i dati all'applicazione e viceversa.

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

    • Se stai usando il livello di astrazione DOP 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 riguardante utf8mb4/ utf8vale come sopra.

Uscita :

  • Se l'applicazione trasmette del testo ad altri sistemi, dovranno anche 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 usare l' default_charsetopzione php.ini o emettere manualmente l' Content-Typeintestazione MIME, che è solo più lavoro ma ha lo stesso effetto.

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

Input :

  • Sfortunatamente, dovresti verificare ogni stringa ricevuta come 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 in modo affidabile.

  • Dalla mia lettura delle attuali specifiche HTML , i seguenti sotto-punti elenco non sono necessari o addirittura più 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, ecc.), Questi punti potrebbero comunque essere utili:

    • Solo per HTML prima di HTML5 : tutti i dati inviati dai browser devono essere in UTF-8. Purtroppo, se si va dal 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 : si noti che le specifiche HTML del W3C affermano che i client "dovrebbero" impostare di default l'invio di moduli al server in qualunque set di caratteri servito dal server, ma questa è apparentemente solo una raccomandazione, quindi la necessità di essere espliciti su ogni singolo <form>etichetta.

Altre considerazioni sul codice :

  • Ovviamente, tutti i file che verranno offerti (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 ampio uso dell'estensione di PHP mbstring.

  • Le operazioni di stringa integrate di PHP non sono sicure per impostazione predefinita UTF-8. Ci sono alcune cose che puoi tranquillamente fare con le normali operazioni di stringa 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. Controlla uno dei collegamenti da utf8.com per alcune buone risorse per imparare tutto ciò che devi sapere.


4
Comprendo che se si specifica il confronto come utf8_ *, codifica automaticamente anche come utf8. È sbagliato?
Chazomaticus,

49
Non sbaglio: COLLATE implica SET DI CARATTERI. Vedere ad esempio dev.mysql.com/doc/refman/5.0/en/charset-database.html .
Chazomaticus,

7
Valuta anche di aggiungere esempi DOP per impostare il set di caratteri.
Ja͢ck,

97
Nota che MySQL non parla la stessa lingua di tutti gli altri. Quando MySQL dice "utf8" significa in realtà "una variante stranamente ritardata di UTF-8 che è limitata a tre byte perché dio sa quale ragione ridicola". Se vuoi davvero UTF-8, dovresti dire a MySQL che vuoi che questa strana cosa piaccia a MySQL chiamare utf8mb4 . Non preoccuparti di salvare sul "WTF!".
R. Martinho Fernandes,

4
Questa risposta mi ha aiutato così tanto MA ho anche scoperto che nel mio caso avevo bisogno di aggiungere JSON_UNESCAPED_UNICODE al mio codice json_encode PHP quando passavo i risultati della query DB tramite ajax.
Petay87,

150

Vorrei aggiungere una cosa all'ottima risposta di Chazomaticus :

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

<meta charset="utf-8">

Sembra banale, ma IE7 mi ha dato problemi in passato.

Stavo facendo tutto bene; il database, la connessione al database e l'intestazione HTTP Content-Type erano tutti impostati su UTF-8 e funzionava bene in tutti gli altri browser, ma Internet Explorer continuava a insistere sull'uso della codifica "Europa occidentale".

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

Modificare:

Il W3C in realtà ha 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:

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


Non dovrebbe anche essere possibile specificare il set di caratteri nelle intestazioni HTTP? Probabilmente ha bisogno di qualche opzione di configurazione per il server web ...
Oliver,

2
@oliver: Sì, puoi inviarlo nell'intestazione HTTP, ma è meglio inviarlo nel contenuto perché se il client salva il file, salverà sempre il metatag. È probabile che un'intestazione HTTP scompaia a meno che il browser non sia abbastanza intelligente da copiarla in un metatag nel file salvato.

5
Inoltre, assicurati che line sia il primo figlio dell'elemento head (prima di qualsiasi roba Unicode). Il browser può reinterpretare la pagina dopo aver colpito quel meta elemento sopra descritto.
alex,

64

Oltre all'impostazione 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 finché ti rendi conto che la maggior parte delle funzioni di stringa non funzionano con Unicode e alcune potrebbero manipolare completamente le stringhe . PHP considera i "caratteri" lunghi 1 byte. A volte questo va bene (ad esempio, explode()cerca solo una sequenza di byte e la utilizza 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 multi-byte che si trovano con Unicode.

Una buona libreria da controllare è phputf8 . Questo riscrive tutte le funzioni "cattive" in modo da poter lavorare in sicurezza su stringhe UTF8. Ci sono estensioni come l'estensione mbstring che cercano di farlo anche per te, ma preferisco usare la libreria perché è più portatile (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.


Configurare l'impostazione di sovraccarico in php.ini. Aiuta quando si utilizzano stringhe multi-byte.
Anthony Rutledge,

32

Ho riscontrato un problema con qualcuno che utilizza PDO e la risposta è stata quella di utilizzare questo 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 ho preso questo è inattivo, ma per fortuna sono stato in grado di ottenerlo utilizzando la cache di Google.


1
Per ulteriori informazioni, questo è necessario solo per le versioni di PHP precedenti alla 5.3.6. Vedi anche: http://stackoverflow.com/a/4361485/2286722 (sebbene utilizzino un $dbh->exec("set names utf8");metodo separato ; preferisco il metodo presentato qui). Btw. c'è anche una nota simile su questo come un commento nel manuale di PHP: php.net/manual/en/pdo.construct.php#96325 .
Marten Koetsier,


24

Nel mio caso, stavo usando mb_split, che usa regex. Pertanto, ho anche dovuto verificare manualmente che la codifica regex fosse utf-8 facendomb_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");.


22

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 , Graphemes , operazioni sulle stringhe , localizzazione e molti altri, vedi sotto.

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

INTL

Buona:

  • Avvolgere la libreria ICU
  • Impostazioni locali standardizzate, impostazione locale per script
  • Formattazione numerica
  • Formattazione della valuta
  • Formattazione dei messaggi (sostituisce gettext)
  • Calendari, date, fuso orario e ora
  • Transliterator
  • Spoofchecker
  • Fasci di risorse
  • convertitori
  • Supporto IDN
  • grafemi
  • confronto
  • iteratori

Cattivo:

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

mb_string

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

ICONV

  • Primario per conversione 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 su tabelle e connessioni (non le regole di confronto). Inoltre, non usare mysql - msqli o PDO
  • postgresql: pg_set_client_encoding
  • sqlite (3): assicurati che sia stato compilato con supporto Unicode e Intl

Alcuni altri Gotchas

  • 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 dalla 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 le funzionalità aggiunte e così via.


2
Sì giusto. Mysqli e DOP possono usare i loro driver nativi. Inoltre possono usare il driver mysqlnd se compilerai php con le --with-mysqli=mysqlnd --with-pdo-mysql=mysqlndopzioni.
Alexander Yancharuk,

14

L'unica cosa che aggiungerei a queste incredibili risposte è enfatizzare il salvataggio dei file nella codifica utf8, ho notato che i browser accettano questa proprietà oltre a impostare 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 uso utf8 senza BOM.

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


13

Di recente 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_ usa MultiByte. Supporta più personaggi ma in generale è un po 'più lento.


9

Ho appena superato lo stesso problema e ho trovato una buona soluzione nei manuali di 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


2
Ho trascorso un'ora cercando di capire un problema di codifica in una pagina su cui sto lavorando e di solito sono abbastanza bravo a capire cose. Consulto sempre questa pagina e la tua risposta mi ha aiutato molto. Ho ottenuto il mio voto. Nel mio caso, set_charset('utf8mb4')non ha funzionato ma >set_charset("utf8")ha funzionato e ciò non è stato effettivamente mostrato nelle altre risposte.
Funk Forty Niner,

@FunkFortyNiner Attenzione: set_charset("utf8")potrebbe funzionare ma si comporterà in modo diverso (vedere le osservazioni sulla differenza tra utf8e utf8mb4e la cronologia delle versioni di mysql). Usa utf8 se devi E SOLO se sai cosa stai facendo !
Martin Hennings,

Soluzione a 5 stelle, stavo leggendo un file di testo riga per riga e ottenendo? per ogni personaggio, quindi ho salvato - come, invece di ansi, ho usato utf8. Grazie.
Atef Farouk, il

8

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 il codice PHP che restituisce l'intestazione. (Oppure puoi aggiungere un tag META ai tuoi documenti HTML.)


Ottimo consiglio sull'impostazione di func_overload: consente una modifica minima al codice esistente.
Simon East,

4
Fai solo attenzione: un po 'di codice potrebbe effettivamente fare affidamento sulla natura a byte per carattere delle funzioni stringa standard.
JW.

È importante notare che la funzione mbstring.func_overload è stata deprecata a partire da PHP 7.2, a causa dei problemi indicati nel commento di @ JW sopra. Quindi il miglior consiglio è: Sì, dovresti assolutamente usare le funzioni mbstring, ma non usare la funzione di sovraccarico per far funzionare le funzioni standard come multibyte.
Simba

6

Il supporto Unicode in PHP è ancora un casino enorme. Sebbene sia in grado di convertire una stringa ISO8859 (che utilizza internamente) in utf8, non ha la capacità di lavorare nativamente con stringhe unicode, il che significa che tutte le funzioni di elaborazione delle stringhe mangeranno e danneggeranno le tue stringhe. Quindi devi utilizzare una libreria separata per il corretto supporto di utf8 o riscrivere tu stesso tutte le funzioni di gestione delle stringhe.

La parte semplice è semplicemente specificare il set di caratteri nelle intestazioni HTTP e nel database e simili, ma nulla di tutto ciò è importante se il tuo codice PHP non genera un 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 'lontano)


6

Se vuoi che il server MySQL decida il set di caratteri e non PHP come client (vecchio comportamento; a mio avviso preferito, preferisci), 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.


5

La risposta migliore è eccellente. Ecco cosa ho dovuto fare su una normale installazione 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 !


1

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 funzionasse):

mysqli_set_charset($con,"utf8");

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

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

in bocca al lupo!


1

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ò è dovuto principalmente al fatto che stai testando i tuoi vecchi dati che sono stati inseriti nel database usando il set di caratteri sbagliato e sono stati convertiti e archiviati in realtà i caratteri del punto interrogativo ?. Ciò significa che hai perso il testo originale per sempre e qualunque cosa tu provi, otterrai ???????.

l'applicazione di ciò che hai appreso dalle risposte di questa domanda a un nuovo dato potrebbe risolvere il tuo problema.


0

Ho avuto questo problema durante la visualizzazione delle tabelle. Ho appena messo questo su ogni variabile di output dell'eco:

<td><?php echo utf8_encode ($Local) ?></td>
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.