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

1050

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

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

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

Показать ещё 2 комментария
Теги:
utf-8

16 ответов

958
Лучший ответ

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

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

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

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

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

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

    • Если вы используете PDO уровень абстракции с помощью PHP & ge; 5.3.6 вы можете указать charset в mysqli, вы можете вызвать set_charset():

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

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

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

Выход

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

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

Ввод

  • К сожалению, вы должны проверить каждую полученную строку как действительную 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, вы делаете это безопасно. Это, к сожалению, тяжелая часть. Вероятно, вы захотите широко использовать расширение PHP mbstring.

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

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

  • 2
    сопоставление не то же самое, что кодирование. Убедитесь, что в вашей базе данных установлена кодировка utf8. Сличение менее важно.
  • 4
    Насколько я понимаю, если вы укажете параметры сортировки как utf8_ *, они также автоматически закодируются как utf8. Это неправильно?
Показать ещё 14 комментариев
138

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

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

<meta charset="utf-8">

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

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

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

Edit:

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

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

  • 0
    Разве нельзя также указать кодировку в заголовках HTTP? Вероятно, нужна некоторая опция конфигурации для веб-сервера ...
  • 2
    @oliver: Да, вы можете отправить его в заголовке HTTP, но лучше отправлять его в контенте, потому что если клиент сохраняет файл, он всегда сохраняет метатег. Заголовок HTTP может просто исчезнуть, если браузер не достаточно умен, чтобы скопировать его в метатег в сохраненном файле.
Показать ещё 3 комментария
56

В дополнение к настройке 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 за кулисами, во всяком случае, для повышения производительности.

  • 0
    Установите настройку перегрузки в php.ini. Это помогает при использовании многобайтовых строк.
26

Старая тема, я знаю. Обнаружена проблема с кем-то, использующим 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.

  • 0
    Глядя на это немного дальше, это необходимо только для версий 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 .
19

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

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

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

INTL

Хорошо:

  • Обертка вокруг библиотеки ICU
  • Стандартизованные локали, задайте локаль за script
  • Форматирование чисел
  • Форматирование валюты
  • Форматирование сообщений (заменяет 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: кодировка и сопоставление таблиц и соединений (не сортировка). Также не используйте 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 .
19

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

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

13

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

Решение заключалось в использовании

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

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

12

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

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

8

Я только что прошел ту же проблему и нашел хорошее решение в руководствах 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") сработал, и это не было показано в других ответах.
  • 0
    @FunkFortyNiner Осторожно: set_charset("utf8") может работать, но будет вести себя по-разному (см. Замечания о разнице между utf8 и utf8mb4 и историей версий mysql). Используйте utf8 если вам нужно И ТОЛЬКО если вы знаете, что делаете !
8

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

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

  • 0
    Отличный совет о настройке func_overload - позволяет минимально модифицировать существующий код.
  • 4
    Просто будьте осторожны - некоторый код может фактически полагаться на однобайтовый характер стандартных строковых функций.
Показать ещё 1 комментарий
7

Хорошая цель - с самого начала - основываясь на характере вашего сайта, я нашел много ресурсов по этому поводу в Googling - вы, конечно, не первый в этом разбираетесь.

Предполагается, что мистический PHP6 должен все это выпрямиться, верно?

Вы можете в значительной степени настроить utf-8 как глобальную кодировку по умолчанию для mysql на уровне сервера, и она по умолчанию будет правильно соответствовать более гранулированным уровням.

6

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

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

5

Если вы хотите, чтобы сервер MySQL решал набор символов, а не PHP как клиент (старое поведение, предпочтительнее, на мой взгляд), попробуйте добавить skip-character-set-client-handshake к my.cnf под [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

Это все!

0
<meta charset="utf-8">

Content-Type: text/html; кодировка = UTF-8

нажмите здесь описание ссылки

-3

Размещено как сообщество wiki:

Для пользователей WordPress:

Sidenote: вопрос был отменен. Сообщение было взято из:

Частичное сообщение:

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


Решение из OP:

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

Требуется обновление wp-config.php. Я изменил define('DB_CHARSET', 'utf8mb4'); на define('DB_CHARSET', 'utf8');

Ещё вопросы

Сообщество Overcoder
Наверх
Меню