UTF-8 w całości

Nov 11 2008

Konfiguruję nowy serwer i chcę w pełni obsługiwać UTF-8 w mojej aplikacji internetowej. Próbowałem tego w przeszłości na istniejących serwerach i zawsze wydaje mi się, że muszę wrócić do ISO-8859-1.

Gdzie dokładnie muszę ustawić kodowanie / zestawy znaków? Zdaję sobie sprawę, że aby to zrobić, muszę skonfigurować Apache, MySQL i PHP - czy jest jakaś standardowa lista kontrolna, którą mogę zastosować, lub może rozwiązać problem, w którym występują niezgodności?

Dotyczy to nowego serwera Linux, na którym działa MySQL 5, PHP, 5 i Apache 2.

Odpowiedzi

1044 chazomaticus Nov 11 2008 at 04:43

Przechowywanie danych :

  • Określ utf8mb4zestaw znaków we wszystkich tabelach i kolumnach tekstowych w bazie danych. To sprawia, że ​​MySQL fizycznie przechowuje i pobiera wartości zakodowane natywnie w UTF-8. Zwróć uwagę, że MySQL użyje niejawnie utf8mb4kodowania, jeśli określono utf8mb4_*sortowanie (bez żadnego jawnego zestawu znaków).

  • W starszych wersjach MySQL (<5.5.3) będziesz niestety zmuszony do używania prostego utf8, który obsługuje tylko podzbiór znaków Unicode. Żałuję, że nie żartowałem.

Dostęp do danych :

  • W kodzie aplikacji (np. PHP), niezależnie od używanej metody dostępu do bazy danych, musisz ustawić kod połączenia na utf8mb4. W ten sposób MySQL nie dokonuje konwersji ze swojego natywnego UTF-8, gdy przekazuje dane do Twojej aplikacji i odwrotnie.

  • Niektóre sterowniki zapewniają własny mechanizm konfigurowania zestawu znaków połączenia, który zarówno aktualizuje swój własny stan wewnętrzny, jak i informuje MySQL o kodowaniu używanym w połączeniu - jest to zazwyczaj preferowane podejście. W PHP:

    • Jeśli używasz warstwy abstrakcji PDO z PHP ≥ 5.3.6, możesz określić charsetw DSN :

       $dbh = new PDO('mysql:charset=utf8mb4');
      
    • Jeśli używasz mysqli , możesz zadzwonić set_charset():

        $mysqli->set_charset('utf8mb4');       // object oriented style
        mysqli_set_charset($link, 'utf8mb4');  // procedural style
      
    • Jeśli utkniesz ze zwykłym mysql, ale akurat korzystasz z PHP ≥ 5.2.3, możesz zadzwonić mysql_set_charset.

  • Jeśli kierowca nie przewiduje własny mechanizm ustawiania zestawu znaków połączenia, być może trzeba będzie wydać zapytanie do powiedzenia MySQL jak aplikacja oczekuje dane dotyczące połączenia mają być zakodowane: SET NAMES 'utf8mb4'.

  • Ta sama uwaga dotycząca utf8mb4/ utf8dotyczy jak powyżej.

Wyjście :

  • Jeśli Twoja aplikacja przesyła tekst do innych systemów, będą one również musiały zostać poinformowane o kodowaniu znaków. W przypadku aplikacji internetowych przeglądarka musi być informowana o kodowaniu, w jakim przesyłane są dane (za pomocą nagłówków odpowiedzi HTTP lub metadanych HTML ).

  • W PHP możesz użyć default_charsetopcji php.ini lub samodzielnie wydać Content-Typenagłówek MIME, co jest po prostu bardziej wymagające, ale daje ten sam efekt.

  • Podczas kodowania danych wyjściowych za pomocą json_encode()dodaj JSON_UNESCAPED_UNICODEjako drugi parametr.

Wejście :

  • Niestety, powinieneś zweryfikować każdy otrzymany ciąg jako poprawny UTF-8, zanim spróbujesz go zapisać lub użyć w dowolnym miejscu. PHP mb_check_encoding()załatwia sprawę, ale musisz go używać religijnie. Naprawdę nie ma sposobu na obejście tego problemu, ponieważ złośliwi klienci mogą przesyłać dane w dowolnym kodowaniu, a ja nie znalazłem sposobu, aby PHP zrobił to niezawodnie.

  • Z mojego czytania aktualnej specyfikacji HTML , poniższe podpunkty nie są już potrzebne ani nawet poprawne dla współczesnego HTML. Rozumiem, że przeglądarki będą działać z danymi w zestawie znaków określonym dla dokumentu i przesyłać je. Jeśli jednak kierujesz reklamy na starsze wersje HTML (XHTML, HTML4 itp.), Poniższe punkty mogą być nadal przydatne:

    • Tylko dla HTML przed HTML5 : chcesz, aby wszystkie dane wysyłane do Ciebie przez przeglądarki były w UTF-8. Niestety, jeśli przejdziesz przez to jedyny sposób, aby to zrobić jest niezawodnie dodać accept-charsetatrybut do wszystkich <form>tagów: <form ... accept-charset="UTF-8">.
    • Tylko dla HTML przed HTML5 : zwróć uwagę, że specyfikacja W3C HTML mówi, że klienci „powinni” domyślnie wysyłać formularze z powrotem do serwera w jakimkolwiek zestawie znaków obsługiwanym przez serwer, ale jest to najwyraźniej tylko zalecenie, stąd potrzeba wyraźnego wyrażenia na każdym pojedynczym <form>etykietka.

Inne kwestie związane z kodem :

  • Oczywiście wszystkie pliki, które będziesz serwować (PHP, HTML, JavaScript itp.) Powinny być zakodowane w prawidłowym UTF-8.

  • Musisz się upewnić, że za każdym razem, gdy przetwarzasz łańcuch UTF-8, robisz to bezpiecznie. To jest niestety najtrudniejsza część. Prawdopodobnie będziesz chciał szeroko korzystać z mbstringrozszerzenia PHP .

  • Wbudowane operacje PHP na łańcuchach nie są domyślnie bezpieczne w UTF-8. Jest kilka rzeczy, które możesz bezpiecznie zrobić za pomocą zwykłych operacji na napisach PHP (takich jak konkatenacja), ale w większości przypadków powinieneś użyć równoważnej mbstringfunkcji.

  • Aby wiedzieć, co robisz (czytaj: nie psuj tego), naprawdę musisz wiedzieć, jak działa UTF-8 na najniższym możliwym poziomie. Sprawdź dowolne linki z utf8.com, aby uzyskać dobre zasoby, aby dowiedzieć się wszystkiego, co musisz wiedzieć.

157 mercator Nov 13 2008 at 02:27

Chciałbym dodać jedną rzecz do doskonałej odpowiedzi Chazomaticus :

Nie zapomnij też o tagu META (takim jak ten lub jego wersji HTML4 lub XHTML ):

<meta charset="utf-8">

Wydaje się to trywialne, ale już wcześniej IE7 sprawiało mi z tym problemy.

Robiłem wszystko dobrze; baza danych, połączenie z bazą danych i nagłówek HTTP Content-Type były ustawione na UTF-8 i działały dobrze we wszystkich innych przeglądarkach, ale Internet Explorer nadal nalegał na używanie kodowania „zachodnioeuropejskiego”.

Okazało się, że na stronie brakuje tagu META. Dodanie tego rozwiązało problem.

Edytować:

W3C faktycznie ma dość dużą sekcję poświęconą I18N . Mają wiele artykułów związanych z tym problemem - opisujących stronę HTTP, (X) HTML i CSS:

  • Często zadawane pytania: Zmiana kodowania strony (X) HTML na UTF-8
  • Deklarowanie kodowania znaków w HTML
  • Samouczek: Zestawy znaków i kodowanie w XHTML, HTML i CSS
  • Ustawianie parametru zestawu znaków HTTP

Zalecają używanie zarówno nagłówka HTTP, jak i metatagu HTML (lub deklaracji XML w przypadku XHTML obsługiwanego jako XML).

66 chroder Nov 11 2008 at 04:30

Oprócz ustawienia default_charsetw php.ini, możesz wysłać poprawny zestaw znaków używając header()z kodu, przed jakimkolwiek wyjściem:

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

Praca z Unicode w PHP jest łatwa, o ile zdasz sobie sprawę, że większość funkcji tekstowych nie działa z Unicode, a niektóre mogą całkowicie zmieniać ciągi znaków . PHP uważa, że ​​"znaki" mają długość 1 bajtu. Czasami jest to w porządku (na przykład explode()szuka tylko sekwencji bajtów i używa jej jako separatora - więc nie ma znaczenia, jakich znaków faktycznie szukasz). Ale innym razem, gdy funkcja jest faktycznie zaprojektowana do pracy ze znakami , PHP nie ma pojęcia, że ​​twój tekst zawiera znaki wielobajtowe, które można znaleźć w Unicode.

Dobrą biblioteką do sprawdzenia jest phputf8 . Powoduje to przepisanie wszystkich „złych” funkcji, dzięki czemu można bezpiecznie pracować na łańcuchach UTF8. Istnieją rozszerzenia, takie jak rozszerzenie mbstring, które również próbują to zrobić za Ciebie, ale wolę korzystać z biblioteki, ponieważ jest bardziej przenośna (ale piszę produkty przeznaczone na rynek masowy, więc jest to dla mnie ważne). Ale phputf8 i tak może używać mbstring za kulisami, aby zwiększyć wydajność.

37 JimW. Sep 11 2012 at 22:40

Znalazłem problem z kimś używającym PDO i odpowiedzią było użycie tego dla parametrów połączenia PDO:

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

Witryna, z której to wziąłem, nie działa, ale na szczęście udało mi się ją pobrać za pomocą pamięci podręcznej Google.

25 JDelage Feb 24 2012 at 05:20

W moim przypadku użyłem mb_split, który używa wyrażenia regularnego. Dlatego musiałem również ręcznie upewnić się, że kodowanie regex to utf-8, robiącmb_regex_encoding('UTF-8');

Na marginesie, podczas uruchamiania odkryłem również, mb_internal_encoding()że wewnętrzne kodowanie nie było utf-8 i zmieniłem to, uruchamiając mb_internal_encoding("UTF-8");.

23 JimmyKane Jan 27 2014 at 16:16

Przede wszystkim, jeśli jesteś w <5,3 PHP, nie. Masz mnóstwo problemów do rozwiązania.

Dziwię się, że nikt nie wspomniał o bibliotece intl , takiej, która ma dobre wsparcie dla Unicode , grafemów , operacji na łańcuchach , lokalizacji i wielu innych, patrz poniżej.

Zacytuję trochę informacji o obsłudze Unicode w PHP na slajdach Elizabeth Smith w PHPBenelux'14

INTL

Dobrze:

  • Opakowanie wokół biblioteki ICU
  • Standaryzowane ustawienia regionalne, ustawianie ustawień regionalnych dla każdego skryptu
  • Formatowanie liczb
  • Formatowanie waluty
  • Formatowanie wiadomości (zastępuje gettext)
  • Kalendarze, daty, strefa czasowa i czas
  • Transliterator
  • Spoofchecker
  • Pakiety zasobów
  • Konwertery
  • Wsparcie IDN
  • Grafemy
  • Porównanie
  • Iteratory

Zły:

  • Nie obsługuje zend_multibite
  • Nie obsługuje konwersji danych wejściowych i wyjściowych HTTP
  • Nie obsługuje przeciążania funkcji

mb_string

  • Włącza obsługę zend_multibyte
  • Obsługuje przezroczyste kodowanie wejścia / wyjścia HTTP
  • Dostarcza otoki dla funtionallity, takie jak strtoupper

ICONV

  • Podstawowy do konwersji zestawu znaków
  • Program obsługi buforu wyjściowego
  • funkcja kodowania MIME
  • konwersja
  • niektóre pomocniki ciągów (len, substr, strpos, strrpos)
  • Filtr strumienia stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

BAZY DANYCH

  • mysql: Charset i collation w tabelach i na połączeniu (nie collation). Nie używaj również mysql - msqli ani PDO
  • postgresql: pg_set_client_encoding
  • sqlite (3): Upewnij się, że został skompilowany z obsługą Unicode i intl

Kilka innych problemów

  • Nie możesz używać nazw plików Unicode z PHP i Windows, chyba że używasz rozszerzenia z trzeciej części.
  • Wyślij wszystko w ASCII, jeśli używasz exec, proc_open i innych wywołań wiersza poleceń
  • Zwykły tekst nie jest zwykłym tekstem, pliki mają kodowanie
  • Możesz konwertować pliki w locie dzięki filtrowi iconv

Zaktualizuję tę odpowiedź na wypadek zmiany dodanych funkcji i tak dalej.

15 PuertoAGP Sep 10 2014 at 10:39

Jedyne, co chciałbym dodać do tych niesamowitych odpowiedzi, to położyć nacisk na zapisywanie plików w kodowaniu utf8. Zauważyłem, że przeglądarki akceptują tę właściwość zamiast ustawiania utf8 jako kodowania kodu. Pokaże ci to każdy przyzwoity edytor tekstu, na przykład Notepad ++ ma opcję menu do kodowania plików, pokazuje aktualne kodowanie i umożliwia zmianę. Dla wszystkich moich plików php używam utf8 bez BOM.

Jakiś czas temu ktoś poprosił mnie o dodanie obsługi utf8 dla aplikacji php / mysql zaprojektowanej przez kogoś innego, zauważyłem, że wszystkie pliki zostały zakodowane w ANSI, więc musiałem użyć ICONV do konwersji wszystkich plików, zmiany tabel bazy danych, aby używać utf8 charset i utf8_general_ci collate, dodaj `` SET NAMES utf8 '' do warstwy abstrakcji bazy danych po połączeniu (jeśli używasz 5.3.6 lub wcześniejszej, w przeciwnym razie musisz użyć charset = utf8 w ciągu połączenia) i zmień funkcje ciągów, aby używać wielobajtowego php odpowiednik funkcji tekstowych.

14 MiguelStevens Jan 13 2014 at 16:37

Niedawno odkryłem, że używanie strtolower()może powodować problemy, gdy dane są obcinane po znaku specjalnym.

Rozwiązaniem było użycie

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

mb_ używa MultiByte. Obsługuje więcej znaków, ale generalnie jest trochę wolniejszy.

10 AbdulSadikYalcin May 06 2015 at 04:36

Właśnie przeszedłem przez ten sam problem i znalazłem dobre rozwiązanie w podręcznikach PHP.

Zmieniłem całe kodowanie plików na UTF8, a następnie domyślne kodowanie w moim połączeniu. To rozwiązało wszystkie problemy.

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

Pokaż źródło

9 JW. Nov 11 2008 at 04:29

W PHP musisz albo użyć funkcji wielobajtowych , albo włączyć mbstring.func_overload . W ten sposób rzeczy takie jak strlen będą działać, jeśli masz znaki, które zajmują więcej niż jeden bajt.

Musisz także określić zestaw znaków swoich odpowiedzi. Możesz użyć AddDefaultCharset, jak powyżej, lub napisać kod PHP, który zwraca nagłówek. (Lub możesz dodać tag META do dokumentów HTML).

7 jalf Nov 11 2008 at 04:48

Obsługa Unicode w PHP to nadal ogromny bałagan. Chociaż jest w stanie przekonwertować ciąg ISO8859 (którego używa wewnętrznie) na utf8, brakuje mu możliwości pracy z ciągami znaków Unicode natywnie, co oznacza, że ​​wszystkie funkcje przetwarzania ciągów będą modyfikować i uszkadzać ciągi. Musisz więc albo użyć oddzielnej biblioteki do właściwej obsługi utf8, albo samodzielnie przepisać wszystkie funkcje obsługujące łańcuchy.

Najłatwiejszą częścią jest po prostu określenie zestawu znaków w nagłówkach HTTP, w bazie danych i tym podobnych, ale nic z tego nie ma znaczenia, jeśli twój kod PHP nie wyświetla prawidłowego UTF8. To najtrudniejsza część, a PHP nie daje praktycznie żadnej pomocy. (Myślę, że PHP6 ma naprawić to, co najgorsze, ale to jeszcze trochę czasu)

7 BudimirGrom Feb 12 2015 at 06:52

Jeśli chcesz, aby serwer MySQL decydował o zestawie znaków, a nie PHP jako klient (stare zachowanie; moim zdaniem preferowane), spróbuj dodać skip-character-set-client-handshakedo swojego my.cnf, poniżej [mysqld]i zrestartuj mysql.

Może to powodować problemy, jeśli używasz czegoś innego niż UTF8.

6 commonpike Jan 14 2011 at 23:13

Najlepsza odpowiedź jest doskonała. Oto, co musiałem zrobić przy zwykłej konfiguracji 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

to było wszystko !

2 castro_pereira Mar 25 2019 at 02:27

jeśli potrzebujesz rozwiązania mysql, miałem podobne problemy z dwoma moimi projektami po migracji serwera. Po przeszukaniu i wypróbowaniu wielu rozwiązań natknąłem się na to jedno / nic, zanim to zadziałało):

mysqli_set_charset($con,"utf8");

Po dodaniu tej linii do mojego pliku konfiguracyjnego wszystko działa dobrze!

Znalazłem to rozwiązanie https://www.w3schools.com/PHP/func_mysqli_set_charset.asp kiedy szukałem rozwiązania wstawiania z zapytania html

powodzenia!

Accountantم Aug 24 2019 at 02:10

Tylko uwaga:

Stoją problemu swoimi spoza alfabetu łacińskiego jest pokazujący jak ?????????, ty zadał pytanie, a on został zamknięty z odniesieniem do tej kanonicznej pytanie, próbowałem wszystkiego i nie ważne co robisz jeszcze dostać ??????????od MySQL.

Dzieje się tak głównie dlatego, że testujesz na starych danych, które zostały wstawione do bazy danych przy użyciu niewłaściwego zestawu znaków i zostały przekonwertowane i zapisane w postaci znaków zapytania ?. Co oznacza, że ​​straciłeś swój oryginalny tekst na zawsze i bez względu na to, co spróbujesz, otrzymasz ???????.

ponowne zastosowanie tego, czego nauczyłeś się z odpowiedzi na to pytanie, na nowych danych może rozwiązać problem.

IjazAhmedBhatti Sep 27 2020 at 13:24

w connection.php: mysqli_set_charset ($ con, „utf8”); i w sortowaniu sql utf = 8