UTF-8 w całości
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
Przechowywanie danych :
Określ
utf8mb4
zestaw 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 niejawnieutf8mb4
kodowania, jeśli określonoutf8mb4_*
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ć
charset
w 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
/utf8
dotyczy 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-Type
nagłówek MIME, co jest po prostu bardziej wymagające, ale daje ten sam efekt.Podczas kodowania danych wyjściowych za pomocą
json_encode()
dodajJSON_UNESCAPED_UNICODE
jako 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-charset
atrybut 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.
- 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ć
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
mbstring
funkcji.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ć.
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).
Oprócz ustawienia default_charset
w 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ść.
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.
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");
.
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.
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.
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.
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
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).
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)
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-handshake
do swojego my.cnf
, poniżej [mysqld]
i zrestartuj mysql
.
Może to powodować problemy, jeśli używasz czegoś innego niż UTF8.
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 !
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!
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.
w connection.php: mysqli_set_charset ($ con, „utf8”); i w sortowaniu sql utf = 8