UTF-8 полностью

1191

Я настраиваю новый сервер и хочу полностью поддерживать UTF-8 в моем веб-приложении. Я пытался сделать это в прошлом на существующих серверах, и мне всегда приходилось прибегать к ISO-8859-1.

Где именно мне нужно установить кодировку / кодировки? Я знаю, что для этого мне нужно настроить Apache, MySQL и PHP - есть ли какой-нибудь стандартный контрольный список, которому я могу следовать, или, возможно, устранить неполадки в случае несоответствия?

Это для нового сервера Linux, работающего под управлением MySQL 5, PHP, 5 и Apache 2.

Меркуцио
источник
8
Вот краткий обзор всех ошибок кодирования, которые вы можете совершить: sebastianviereck.de/en/…
Sebastian Viereck
13
Вот введение в кодировки в целом и кодировки в PHP в частности: что нужно знать каждому программисту абсолютно
точно
Некоторые недавние дискуссии по поводу PHP 7 показывают, что нет никаких изменений в «официально заброшенной» позиции 2010 года ... Есть что-то еще о «PHP7 и UTF-8»?
Питер Краусс
Эта проблема распространена. Но быстрого решения не существует, вам придется настраивать utf-8каждый из них отдельно - MySQL 5, PHP 5 ИЛИ Apache 2.
Маниш Шривастава

Ответы:

1016

Хранение данных :

  • Укажите utf8mb4набор символов для всех таблиц и текстовых столбцов в вашей базе данных. Это заставляет MySQL физически хранить и извлекать значения, изначально закодированные в UTF-8. Обратите внимание, что MySQL будет неявно использовать utf8mb4кодировку, если utf8mb4_*указано сопоставление (без какого-либо явного набора символов).

  • В более старых версиях MySQL (<5.5.3), к сожалению, вы будете вынуждены использовать просто utf8, который поддерживает только подмножество символов Unicode. Я хотел бы шутить.

Доступ к данным :

  • В коде вашего приложения (например, PHP), в каком бы методе доступа к БД вы ни использовали, вам нужно установить кодировку соединения в utf8mb4. Таким образом, MySQL не выполняет преобразование из своего собственного UTF-8, когда передает данные в ваше приложение, и наоборот.

  • Некоторые драйверы предоставляют собственный механизм для настройки набора символов соединения, который обновляет свое собственное внутреннее состояние и сообщает MySQL о кодировке, которая будет использоваться в соединении - обычно это предпочтительный подход. В PHP:

    • Если вы используете уровень абстракции PDO с PHP ≥ 5.3.6, вы можете указать charsetв DSN :

      $dbh = new PDO('mysql:charset=utf8mb4');
    • Если вы используете mysqli , вы можете позвонить set_charset():

      $mysqli->set_charset('utf8mb4');       // object oriented style
      mysqli_set_charset($link, 'utf8mb4');  // procedural style
    • Если вы застряли с обычным mysql, но случайно запустили PHP ≥ 5.2.3, вы можете позвонить mysql_set_charset.

  • Если драйвер не предоставляет свой собственный механизм для установки набора символов соединения, возможно , придется выдать запрос , чтобы сказать MySQL , как ваше приложение ожидает данные о связи должны быть закодированы: SET NAMES 'utf8mb4'.

  • То же самое касается utf8mb4/ utf8применяется, как указано выше.

Выход :

  • Если ваше приложение передает текст в другие системы, они также должны быть проинформированы о кодировке символов. В веб-приложениях браузер должен быть проинформирован о кодировке, в которой отправляются данные (через заголовки ответа HTTP или метаданные HTML ).

  • В PHP вы можете использовать default_charsetопцию php.ini или вручную выдать Content-Typeзаголовок MIME, который просто больше работает, но имеет тот же эффект.

  • При кодировании вывода с использованием json_encode()добавьте JSON_UNESCAPED_UNICODEв качестве второго параметра.

Вход :

  • К сожалению, вы должны проверить каждую полученную строку как действительную UTF-8, прежде чем пытаться сохранить ее или использовать где-либо. PHP mb_check_encoding()делает свое дело, но вы должны использовать его религиозно. Это действительно невозможно, поскольку злонамеренные клиенты могут отправлять данные в любой нужной им кодировке, и я не нашел способа заставить PHP сделать это для вас надежно.

  • Из моего прочтения текущей спецификации HTML следующие подпункты больше не нужны и даже не действительны для современного HTML. Насколько я понимаю, браузеры будут работать и отправлять данные в наборе символов, указанном для документа. Однако, если вы ориентируетесь на более старые версии HTML (XHTML, HTML4 и т. Д.), Эти пункты могут быть полезны:

    • Только для HTML до HTML5 : вы хотите, чтобы все данные, отправляемые вам браузерами, были в формате UTF-8. К сожалению, если вы идете на единственный способ надежно сделать это добавить accept-charsetатрибут всех <form>тегов: <form ... accept-charset="UTF-8">.
    • Только для HTML до HTML5 : обратите внимание, что спецификация HTML W3C гласит, что клиенты «должны» по умолчанию отправлять формы обратно на сервер с любым набором символов, который обслуживал сервер, но это, очевидно, только рекомендация, следовательно, необходимость явного указания на каждом <form>тег.

Другие соображения по коду :

  • Очевидно, что все файлы, которые вы будете обслуживать (PHP, HTML, JavaScript и т. Д.), Должны быть закодированы в допустимом UTF-8.

  • Вам нужно убедиться, что каждый раз, когда вы обрабатываете строку UTF-8, вы делаете это безопасно. Это, к сожалению, самая сложная часть. Возможно, вы захотите широко использовать mbstringрасширение PHP .

  • Встроенные строковые операции PHP не являются безопасными по умолчанию в UTF-8. Есть некоторые вещи, которые вы можете безопасно делать с обычными строковыми операциями PHP (например, конкатенация), но для большинства вещей вы должны использовать эквивалентную mbstringфункцию.

  • Чтобы знать, что вы делаете (читай: не путайте), вам действительно нужно знать UTF-8 и как он работает на самом низком уровне. Проверьте любую из ссылок с utf8.com для некоторых хороших ресурсов, чтобы узнать все, что вам нужно знать.

chazomaticus
источник
4
Насколько я понимаю, если вы укажете параметры сортировки как utf8_ *, они также автоматически закодируются как utf8. Это неправильно?
Chazomaticus
49
Я не ошибаюсь: COLLATE подразумевает набор символов. См., Например, dev.mysql.com/doc/refman/5.0/en/charset-database.html .
Chazomaticus
7
Попробуйте добавить примеры PDO для установки набора символов.
Яцк
97
Обратите внимание, что MySQL не говорит на том же языке, что и все остальные. Когда MySQL говорит «utf8», это действительно означает «какой-то странно запаздывающий вариант UTF-8, который ограничен тремя байтами, потому что бог знает, какая нелепая причина». Если вы действительно хотите UTF-8, вы должны сказать MySQL, что вы хотите эту странную вещь, которую MySQL любит называть utf8mb4 . Не беспокойтесь о экономии на "WTF!"
Р. Мартиньо Фернандес
4
Этот ответ мне очень помог, НО я также обнаружил, что в моем случае мне нужно было добавить JSON_UNESCAPED_UNICODE в мой json_encode PHP при передаче результатов запроса БД обратно через ajax.
Petay87
150

Я хотел бы добавить одну вещь к отличному ответу chazomaticus :

Также не забудьте мета-тег (например, или его HTML4 или XHTML-версию ):

<meta charset="utf-8">

Это кажется тривиальным, но IE7 доставлял мне проблемы с этим раньше.

Я все делал правильно; база данных, соединение с базой данных и HTTP-заголовок Content-Type были настроены на UTF-8, и он прекрасно работал во всех других браузерах, но Internet Explorer по-прежнему настаивал на использовании кодировки «западноевропейский».

Оказалось, что на странице отсутствовал тег META. Добавление, которое решило проблему.

Редактировать:

На самом деле W3C имеет довольно большой раздел, посвященный I18N . У них есть несколько статей, связанных с этой проблемой - описывающих стороны HTTP, (X) HTML и CSS:

Они рекомендуют использовать как заголовок HTTP, так и метатег HTML (или объявление XML, если XHTML используется в качестве XML).

Меркатора
источник
Разве нельзя также указать кодировку в заголовках HTTP? Вероятно, нужна некоторая опция конфигурации для веб-сервера ...
oliver
2
@oliver: Да, вы можете отправить его в заголовке HTTP, но лучше отправлять его в контенте, потому что если клиент сохраняет файл, он всегда сохраняет метатег. Заголовок HTTP может просто исчезнуть, если браузер не достаточно умен, чтобы скопировать его в метатег в сохраненном файле.
5
Кроме того, убедитесь, что строка является первым дочерним элементом элемента head (перед любым элементом Unicode). Браузер может переосмыслить страницу после нажатия на метаэлемент, описанный выше.
Alex
64

В дополнение к настройке default_charsetв php.ini, вы можете отправлять правильную кодировку, используя header()код, перед любым выводом:

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

Работать с Unicode в PHP легко, если вы понимаете, что большинство строковых функций не работают с Unicode, а некоторые могут полностью искажать строки . PHP считает "символы" длиной 1 байт. Иногда это нормально (например, explode()только ищет последовательность байтов и использует ее в качестве разделителя - поэтому не имеет значения, какие именно символы вы ищете). Но в других случаях, когда функция фактически предназначена для работы с символами , PHP не знает, что в вашем тексте есть многобайтовые символы, которые можно найти в Unicode.

Хорошая библиотека для проверки - phputf8 . Это переписывает все «плохие» функции, чтобы вы могли безопасно работать со строками UTF8. Существуют такие расширения, как расширение mbstring, которые пытаются сделать это и для вас, но я предпочитаю использовать библиотеку, потому что она более переносима (но я пишу продукты для массового рынка, поэтому это важно для меня). Но phputf8 может использовать mbstring за кулисами, чтобы увеличить производительность.

chroder
источник
Установите настройку перегрузки в php.ini. Это помогает при использовании многобайтовых строк.
Энтони Ратледж
32

Я обнаружил проблему с кем-то, использующим PDO, и ответ состоял в том, чтобы использовать это для строки подключения PDO:

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

Сайт, с которого я взял это, не работает, но, к счастью, я смог получить его с помощью кеша Google.

Джим В.
источник
1
Глядя на это немного дальше, это необходимо только для версий PHP до 5.3.6. Смотрите также: http://stackoverflow.com/a/4361485/2286722 (хотя они используют отдельный $dbh->exec("set names utf8");; я предпочитаю метод, представленный здесь). Btw. на это есть также примечание в виде комментария в руководстве по PHP: php.net/manual/en/pdo.construct.php#96325 .
Мартен Коецер
Смотрите также ответ Палека на PDO + MySQL и неработающую кодировку UTF-8 .
Питер Мортенсен
24

В моем случае я использовал mb_split, который использует регулярные выражения. Поэтому я также должен был вручную убедиться, что кодировка регулярного выражения была utf-8, выполнивmb_regex_encoding('UTF-8');

Как примечание, я также обнаружил, запустив, mb_internal_encoding()что внутренняя кодировка не была utf-8, и я изменил это, запустив mb_internal_encoding("UTF-8");.

JDelage
источник
22

Прежде всего, если вы находитесь в <5.3PHP, то нет. У вас есть куча проблем для решения.

Я удивлен, что никто не упомянул библиотеку intl , которая имеет хорошую поддержку юникода , графем , строковых операций , локализации и многих других, см. Ниже.

Я процитирую некоторую информацию о поддержке юникода в PHP на слайдах Элизабет Смит на PHPBenelux'14

INTL

Хорошо:

  • Обертка вокруг библиотеки ICU
  • Стандартные локали, установить локаль для скрипта
  • Форматирование чисел
  • Форматирование валюты
  • Форматирование сообщения (заменяет gettext)
  • Календари, даты, часовой пояс и время
  • Транслитератор
  • Spoofchecker
  • Ресурсные связки
  • конвертеры
  • Поддержка IDN
  • графем
  • сличение
  • итераторы

Плохой:

  • Не поддерживает zend_multibite
  • Не поддерживает преобразование ввода-вывода HTTP
  • Не поддерживает перегрузку функций

mb_string

  • Включает поддержку zend_multibyte
  • Поддерживает прозрачную кодировку HTTP in / out
  • Обеспечивает некоторые обертки для funtionallity, такие как strtoupper

Iconv

  • Основной для преобразования кодировки
  • Обработчик буфера вывода
  • функциональность кодирования MIME
  • преобразование
  • некоторые помощники по строкам (len, substr, strpos, strrpos)
  • Потоковый фильтр stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

БАЗ

  • mysql: Charset и сопоставление на таблицах и на соединении (не сопоставление). Также не используйте mysql - msqli или PDO
  • postgresql: pg_set_client_encoding
  • sqlite (3): убедитесь, что он был скомпилирован с поддержкой Unicode и Intl

Некоторые другие Gotchas

  • Вы не можете использовать имена файлов Unicode с PHP и Windows, если вы не используете расширение третьей части.
  • Отправить все в ASCII, если вы используете exec, proc_open и другие вызовы командной строки
  • Простой текст не простой текст, файлы имеют кодировки
  • Вы можете конвертировать файлы на лету с фильтром iconv

Я обновлю этот ответ на случай, если что-то изменится, добавятся функции и так далее.

Джимми Кейн
источник
2
Да, верно. Mysqli и PDO могут использовать свои родные драйверы. Также они могут использовать драйвер mysqlnd, если вы скомпилируете php с --with-mysqli=mysqlnd --with-pdo-mysql=mysqlndопциями.
Александр Янчарук
14

Единственное, что я хотел бы добавить к этим удивительным ответам, - это сделать акцент на сохранении ваших файлов в кодировке utf8. Я заметил, что браузеры принимают это свойство вместо установки utf8 в качестве кодировки кода. Любой приличный текстовый редактор покажет вам это, например, Notepad ++ имеет пункт меню для добавления файла, показывает текущую кодировку и позволяет вам ее изменить. Для всех моих php-файлов я использую utf8 без спецификации.

Некоторое время назад я попросил кого-то попросить меня добавить поддержку utf8 для приложения php / mysql, разработанного кем-то другим, я заметил, что все файлы были закодированы в ANSI, поэтому мне пришлось использовать ICONV для преобразования всех файлов, изменить таблицы базы данных, чтобы использовать utf8 charset и utf8_general_ci collate, добавьте «SET NAMES utf8» к уровню абстракции базы данных после соединения (если вы используете 5.3.6 или более раннюю версию, вы должны использовать charset = utf8 в строке соединения) и изменить строковые функции, чтобы использовать многобайтовый php строковые функции эквивалентны.

Пуэрто АГП
источник
13

Я недавно обнаружил, что с помощью strtolower() может вызвать проблемы, когда данные усекаются после специального символа.

Решение было использовать

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

mb_ использует MultiByte. Он поддерживает больше символов, но в целом немного медленнее.

Мигель Стивенс
источник
9

Я только что прошел ту же самую проблему и нашел хорошее решение в руководствах PHP.

Я изменил кодировку всех файлов на UTF8, а затем кодировку по умолчанию для моего соединения. Это решило все проблемы.

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

Просмотреть исходный код

Абдул Садик Ялчин
источник
2
Я потратил час, пытаясь выяснить проблему с кодировкой на странице, над которой я работаю, и я обычно довольно хорошо разбираюсь в вещах. Я всегда просматриваю эту страницу, и ваш ответ мне очень помог. Получил мой голос. В моем случае set_charset('utf8mb4')это не сработало, но сработало >set_charset("utf8"), и это не было показано в других ответах.
Funk Forty
@FunkFortyNiner Осторожно: set_charset("utf8")может работать, но будет вести себя по-другому (см. Замечания о разнице между utf8и utf8mb4и историей версий mysql). Используйте, utf8 если вам нужно И ТОЛЬКО если вы знаете, что делаете !
Мартин Хеннингс
Решение 5 звезд, я читал текстовый файл построчно и получал? тогда для каждого символа я делал save-as, а вместо ansi использовал utf8. Спасибо.
Атеф Фарук
8

В PHP вам нужно либо использовать многобайтовые функции , либо включить mbstring.func_overload . Таким образом, такие вещи, как strlen, будут работать, если у вас есть символы, которые занимают более одного байта.

Вам также необходимо определить набор символов ваших ответов. Вы можете использовать AddDefaultCharset, как указано выше, или написать код PHP, который возвращает заголовок. (Или вы можете добавить мета-тег в ваши HTML-документы.)

JW.
источник
Отличный совет о настройке func_overload - позволяет минимально модифицировать существующий код.
Саймон Ист
4
Только будьте осторожны - некоторый код может фактически полагаться на однобайтовый характер стандартных строковых функций.
JW.
Важно отметить, что функция mbstring.func_overload устарела с версии PHP 7.2 из-за проблем, отмеченных в комментарии @ JW выше. Итак, лучший совет: да, вам определенно следует использовать функции mbstring, но не используйте функцию перегрузки, чтобы стандартные функции работали как многобайтовые.
Симба
6

Поддержка Unicode в PHP по-прежнему огромная путаница. Хотя он способен конвертировать строку ISO8859 (которую он использует внутри) в utf8, ему не хватает возможности работать со строками Unicode изначально, что означает, что все функции обработки строк будут искажать и портить ваши строки. Таким образом, вы должны либо использовать отдельную библиотеку для правильной поддержки utf8, либо переписать все функции обработки строк самостоятельно.

Самая простая часть - просто указать кодировку в заголовках HTTP и в базе данных и тому подобное, но ничего из этого не имеет значения, если ваш код PHP не выводит допустимый UTF8. Это сложная часть, и PHP там практически не помогает. (Я думаю, что PHP6 должен исправить худшее из этого, но это еще далеко)

jalf
источник
6

Если вы хотите, чтобы сервер MySQL определял набор символов, а не PHP как клиент (старое поведение; на мой взгляд, предпочтительнее), попробуйте добавить его skip-character-set-client-handshakeв раздел my.cnfunder [mysqld]и перезапустить mysql.

Это может вызвать проблемы в случае, если вы используете что-то кроме UTF8.

Будимир Гром
источник
5

Главный ответ отлично. Вот что мне нужно было сделать на обычной установке 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

это все !

commonpike
источник
1

Если вы хотите решение MySQL, у меня были аналогичные проблемы с 2 из моих проектов, после миграции сервера. После поиска и опробования множества решений я наткнулся на это / ничего до того, как это сработало):

mysqli_set_charset($con,"utf8");

После добавления этой строки в мой конфигурационный файл все работает отлично!

Я нашел это решение https://www.w3schools.com/PHP/func_mysqli_set_charset.asp, когда искал решение для вставки из запроса HTML

удачи!

castro_pereira
источник
1

Просто примечание:

Вы столкнулись с проблемой ваших не латинских символов , показывая , как ?????????вы задали вопрос, и он получил закрыт со ссылкой на этот канонический вопрос, вы перепробовали все , и независимо от того , что вы делаете , вы все еще получаете ??????????от MySQL.

Это в основном потому, что вы тестируете свои старые данные которые были вставлены в базу данных с использованием неправильной кодировки, а также преобразованы и сохранены в символы знака вопроса ?. Это означает, что вы потеряли свой оригинальный текст навсегда, и независимо от того, что вы пытаетесь, вы получите ???????.

Применение того, что вы узнали из ответов на этот вопрос на свежие данные, может решить вашу проблему.

Бухгалтер م
источник
0

У меня была эта проблема при отображении таблиц. Я просто поместил это в каждую переменную вывода эха:

<td><?php echo utf8_encode ($Local) ?></td>
Жоао Фонсека
источник