UTF-8 полностью
Я настраиваю новый сервер и хочу полностью поддерживать UTF-8 в моем веб-приложении. Я пытался сделать это в прошлом на существующих серверах, и мне всегда приходилось прибегать к ISO-8859-1.
Где именно мне нужно установить кодировку / кодировки? Я знаю, что для этого мне нужно настроить Apache, MySQL и PHP - есть ли какой-нибудь стандартный контрольный список, которому я могу следовать, или, возможно, устранить неполадки в случае несоответствия?
Это для нового сервера Linux, работающего под управлением MySQL 5, PHP, 5 и Apache 2.
13 ответов
Хранение данных:
Укажите
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 заголовок самостоятельно, который просто больше работы, но имеет тот же эффект.
Вход:
К сожалению, вы должны проверить каждую полученную строку как действительную UTF-8, прежде чем пытаться сохранить ее или использовать где-либо. РНР
mb_check_encoding()
делает трюк, но вы должны использовать это религиозно. Это действительно невозможно, поскольку злонамеренные клиенты могут отправлять данные в любой нужной им кодировке, и я не нашел способа заставить PHP сделать это для вас надежно.Из моего прочтения текущей спецификации HTML следующие подпункты больше не нужны и даже не действительны для современного HTML. Насколько я понимаю, браузеры будут работать и отправлять данные в наборе символов, указанном для документа. Однако, если вы ориентируетесь на более старые версии HTML (XHTML, HTML4 и т. Д.), Эти пункты могут быть полезны:
- Только для HTML до HTML5: вы хотите, чтобы все данные, отправляемые вам браузерами, были в UTF-8. К сожалению, если вы выберете единственный способ надежно это сделать, это добавить
accept-charset
приписать всем своим<form>
теги:<form ... accept-charset="UTF-8">
, - Только для HTML до HTML5: обратите внимание, что спецификация W3C HTML гласит, что клиенты "должны" по умолчанию отправлять формы обратно на сервер с любым набором символов, который обслуживал сервер, но это, очевидно, только рекомендация, следовательно, необходимость явного указания на каждом
<form>
тег.
- Только для HTML до HTML5: вы хотите, чтобы все данные, отправляемые вам браузерами, были в UTF-8. К сожалению, если вы выберете единственный способ надежно это сделать, это добавить
Другие аспекты кода:
Очевидно, что все файлы, которые вы будете обслуживать (PHP, HTML, JavaScript и т. Д.), Должны быть закодированы в допустимом UTF-8.
Вам нужно убедиться, что каждый раз, когда вы обрабатываете строку UTF-8, вы делаете это безопасно. Это, к сожалению, самая сложная часть. Вы, вероятно, захотите широко использовать PHP
mbstring
расширение.Встроенные строковые операции PHP не являются безопасными по умолчанию в UTF-8. Есть некоторые вещи, которые вы можете безопасно делать с обычными строковыми операциями PHP (например, конкатенация), но для большинства вещей вы должны использовать эквивалентные
mbstring
функция.Чтобы знать, что вы делаете (читай: не путайте), вам действительно нужно знать UTF-8 и как он работает на самом низком уровне. Проверьте любую из ссылок с utf8.com для некоторых хороших ресурсов, чтобы узнать все, что вам нужно знать.
Я хотел бы добавить одну вещь к отличному ответу Chazomaticus:
Также не забудьте мета-тег (например, или его HTML4 или XHTML-версию):
<meta charset="utf-8">
Это кажется тривиальным, но IE7 доставлял мне проблемы с этим раньше.
Я все делал правильно; база данных, соединение с базой данных и HTTP-заголовок Content-Type были настроены на UTF-8, и он прекрасно работал во всех других браузерах, но Internet Explorer по-прежнему настаивал на использовании кодировки "западноевропейский".
Оказалось, что на странице отсутствовал тег META. Добавление, которое решило проблему.
Редактировать:
W3C на самом деле имеет довольно большой раздел, посвященный I18N. У них есть ряд статей, связанных с этой проблемой - описывающих стороны HTTP, (X)HTML и CSS:
- FAQ: изменение (X) кодировки HTML-страницы на UTF-8
- Объявление кодировки символов в HTML
- Учебник: наборы символов и кодировки в XHTML, HTML и CSS
- Установка параметра HTTP-кодировки
Они рекомендуют использовать как заголовок HTTP, так и метатег HTML (или объявление XML, если XHTML используется в качестве XML).
В дополнение к настройке 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 за кулисами, чтобы увеличить производительность.
Старая тема, я знаю. Обнаружена проблема с кем-то, использующим 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.
В моем случае я использовал mb_split
, который использует регулярное выражение. Поэтому я также должен был вручную убедиться, что кодировка регулярного выражения была utf-8, выполнив mb_regex_encoding('UTF-8');
Как примечание, я также обнаружил, запустив mb_internal_encoding()
что внутренняя кодировка не была utf-8, и я изменил это, запустив mb_internal_encoding("UTF-8");
,
Прежде всего, если вы находитесь в < 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, если вы не используете расширение 3-й части.
- Отправить все в ASCII, если вы используете exec, proc_open и другие вызовы командной строки
- Простой текст не простой текст, файлы имеют кодировки
- Вы можете конвертировать файлы на лету с фильтром iconv
Я обновлю этот ответ на случай, если что-то изменится, добавятся новые функции и так далее.
Единственное, что я хотел бы добавить к этим удивительным ответам, - это сделать акцент на сохранении ваших файлов в кодировке 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 строковые функции эквивалентны.
Я недавно обнаружил, что с помощью strtolower()
может вызвать проблемы, когда данные усекаются после специального символа.
Решение было использовать
mb_strtolower($string, 'UTF-8');
mb_ использует MultiByte. Он поддерживает больше символов, но в целом немного медленнее.
В PHP вам нужно либо использовать многобайтовые функции, либо включить mbstring.func_overload. Таким образом, такие вещи, как strlen, будут работать, если у вас есть символы, которые занимают более одного байта.
Вам также нужно будет определить набор символов ваших ответов. Вы можете использовать AddDefaultCharset, как указано выше, или написать код PHP, который возвращает заголовок. (Или вы можете добавить мета-тег в ваши HTML-документы.)
Я только что прошел через ту же проблему и нашел хорошее решение в руководствах по 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());
}
Поддержка Unicode в PHP по-прежнему огромная путаница. Хотя он способен конвертировать строку ISO8859 (которую он использует внутри) в utf8, ему не хватает возможности работать со строками Unicode изначально, что означает, что все функции обработки строк будут искажать и повреждать ваши строки. Таким образом, вы должны либо использовать отдельную библиотеку для правильной поддержки utf8, либо переписать все функции обработки строк самостоятельно.
Самая простая часть - просто указать кодировку в заголовках HTTP и в базе данных и тому подобное, но ничего из этого не имеет значения, если ваш код PHP не выводит действительный UTF8. Это сложная часть, и PHP там практически не помогает. (Я думаю, что PHP6 должен исправить худшее из этого, но это еще далеко)
Если вы хотите, чтобы сервер MySQL определял набор символов, а не PHP как клиент (старое поведение; предпочтительнее, на мой взгляд), попробуйте добавить skip-character-set-client-handshake
на ваш my.cnf
, под [mysqld]
и перезагрузите mysql
,
Это может вызвать проблемы в случае, если вы используете что-то другое, кроме UTF8.
Главный ответ отлично. Вот что я должен был сделать на обычной установке 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
это все!
Хорошая цель с самого начала - исходя из характера вашего сайта, я нашел много ресурсов по этому поводу от Googling - конечно, вы не первый, кто с этим сталкивается.
Предполагается, что мистический PHP6 все исправит, верно?
Вы можете в значительной степени установить utf-8 как глобальную кодировку по умолчанию для mysql на уровне сервера, и она будет по умолчанию правильно настроена на более детальные уровни.
Если вы хотите решение MySQL, у меня были аналогичные проблемы с 2 из моих проектов, после миграции сервера. После поиска и опробования множества решений я наткнулся на это / ничего до того, как это сработало):
mysqli_set_charset($con,"utf8");
После добавления этой строки в мой конфигурационный файл все работает отлично!
Я нашел это решение https://www.w3schools.com/PHP/func_mysqli_set_charset.asp когда искал решение для вставки из HTML-запроса
удачи!
Просто примечание:
Вы столкнулись с проблемой, что ваши нелатинские символы отображаются как ?????????
, вы задали вопрос, и он был закрыт ссылкой на этот канонический вопрос, вы перепробовали все, и что бы вы ни делали, вы все равно получаете ??????????
от MySQL
.
Это в основном потому, что вы тестируете свои старые данные, которые были вставлены в базу данных с использованием неправильной кодировки и были преобразованы и сохранены на самом деле в символы вопросительного знака.?
. Это означает, что вы навсегда потеряли исходный текст, и что бы вы ни пытались, вы получите???????
.
Повторное применение того, что вы узнали из ответов на этот вопрос, на свежих данных может решить вашу проблему.
в connection.php: mysqli_set_charset($con, "utf8"); и в sql collation utf=8