php создать файл в кодировке utf 8
How to write file in UTF-8 format?
I have bunch of files that are not in UTF-8 encoding and I’m converting a site to UTF-8 encoding.
I’m using simple script for files that I want to save in utf-8, but the files are saved in old encoding:
How can I save files in utf-8 encoding?
11 Answers 11
file_get_contents / file_put_contents will not magically convert encoding.
Or alternatively, with PHP’s stream filters:
On Unix/Linux a simple shell command could be used alternatively to convert all files from a given directory:
Could be started via PHPs exec() as well.
I got this line from Cool
If you want to use recode recursively, and filter for type, try this:
This is quite useful question. I think that my solution on Windows 10 PHP7 is rather useful for people who have yet some UTF-8 conversion trouble.
Here are my steps. The PHP script calling the following function, here named utfsave.php must have UTF-8 encoding itself, this can be easily done by conversion on UltraEdit.
In utfsave.php, we define a function calling PHP fopen($filename, «wb«), ie, it’s opened in both w write mode, and especially with b in binary mode.
The source file cp936gbktext.txt file content:
Running utf8save.php on Windows 10 PHP, thus created utf8text.txt, utf8text2.txt files will be automatically saved in UTF-8 format.
With this method, BOM char is not required. BOM solution is bad because it causes troubles when we do sourcing an sql file for MySQL for example.
It’s worth noting that I failed making work file_put_contents($filename, utf8_encode($mystring)); for this purpose.
If you don’t know the encoding of the source file, you can list encodings with PHP:
This gives a list like this:
If you cannot guess, you try one by one, as mb_detect_encoding() cannot do the job easily.
UTF-8 в PHP. Часть 1
1. Вступление
Для понимания дальнейшего текста начинающим нужно знать некоторые детали по кодировкам в целом. Подачу материала я постараюсь максимально упростить. Для незнающих ничего о побитовых операциях необходимо предварительно ознакомиться с материалами на википедии.
Начать нужно с понимания того, что компьютер работает с числами и хранить строку (и символ, как её часть) приходиться тоже в числовом виде. Для этих целей существуют кодировки. По сути это таблицы, в которых указано соответствие между числами и символами. Исторически сложилось, что основная кодировка ASCII содержит лишь контрольные коды и латинские символы, всего их 128 (127 – максимальное число, которое можно хранить в 7 битах).
Для того чтобы хранить и другие тексты на основе ASCII было создано много других кодировок, в которых добавили 8-ой бит. Они могут хранить уже до 256 символов, первые 128 с которых традиционно соответствовали ASCII, а вот в остальную часть каждый пихал всё, что ему хотелось. Так и получилось, что у каждого производителя операционных систем свои наборы кодировок, причём каждая удовлетворяла потребности лишь относительно узкого круга людей. Ситуацию ещё сильнее усложнили отсутствием общих стандартов, различать их алгоритмически стало невозможно и теперь это больше похоже на угадывание (об этом в следующих частях).
В итоге потребовался универсальный выход, кодировка, которая сможет хранить все возможные символы и будет учитывать различия в письме различных народов (например, направление письма). Поставленную задачу решили созданием Unicode, которая способна кодировать практически все системы письменности в мире одной кодировкой.
Хотелось бы подробнее остановиться на последнем пункте. Это значит, что если раньше можно было выполнять простое преобразование по таблице и записывать результат, то сейчас определён и метод сохранения этого результата, в зависимости от разрядности, которая требуется для его хранения. На примере принцип хранения вы можете увидеть в таблице (x – хранимые биты данных):
Бит | Максимальное хранимое значение | 1 октет | 2 октет | 3 октет | 4 октет |
---|---|---|---|---|---|
Начальный октет | Продолжающие октеты | ||||
7 | U+007F | 0xxxxxxx | |||
11 | U+07FF | 110xxxxx | 10xxxxxx | ||
16 | U+FFFF | 1110xxxx | 10xxxxxx | 10xxxxxx | |
21 | U+10FFFF (по стандарту, но реально U+1FFFFF) | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
Легко заметить, что в старших битах начального октета всегда находится счётчик, указывающий на количество байт в последовательности – это количество ведущих единиц, после которых идёт ноль. Обратите внимание: если октет лишь один, то ведущая единица не указывается, благодаря чему начальные октеты легко отличить от продолжающих.
Для примера давайте посмотрим как строка «Привет Hi» будет выглядеть в кодировке UTF-8.
Шаг первый. Перевести каждый символ в его числовое представление (я буду использовать шестнадцатеричную систему исчисления) по таблице.
Привет Hi = 0x041F 0x0440 0x0438 0x0432 0x044D 0x0442 0x0020 0x0048 0x0069
Не забываем, что пробел – тоже символ.
Шаг второй. Конвертировать числа из шестнадцатеричной в двоичную систему. Используем калькулятор Windows 7 (в режиме программиста).
0x041F = 0000 0100 0001 1111
0x0440 = 0000 0100 0100 0000
0x0438 = 0000 0100 0011 1000
0x0432 = 0000 0100 0011 0010
0x0435 = 0000 0100 0011 0101
0x0442 = 0000 0100 0100 0010
0x0020 = 0010 0000
0x0048 = 0100 1000
0x0069 = 0110 1001
Для наглядности я добавил нули в старшие разряды. Обратите внимание: символы могут занимать разное количество байт.
Шаг третий. Перевести числовые представления в последовательности октетов UTF-8.
0x041F = 100 0001 1111 = 110xxxxx 10xxxxxx = 11010000 10011111
0x0440 = 100 0100 0000 = 110xxxxx 10xxxxxx = 11010001 10000000
0x0438 = 100 0011 1000 = 110xxxxx 10xxxxxx = 11010000 10111000
0x0432 = 100 0011 0010 = 110xxxxx 10xxxxxx = 11010000 10110010
0x0435 = 100 0011 0101 = 110xxxxx 10xxxxxx = 11010000 10110101
0x0442 = 100 0100 0010 = 110xxxxx 10xxxxxx = 11010001 10000010
0x0020 = 010 0000 = 0xxxxxx = 00100000
0x0048 = 100 1000 = 0xxxxxx = 01001000
0x0069 = 110 1001 = 0xxxxxx = 01101001
Счётчики выделены жирным. Обратите внимание: символы с кодами до 0x0080 сохраняются без изменений, это и есть совместимость с ASCII. Ещё следует понимать, что UTF-8 будет занимать в 2 раза больше места (2 байта) для русскоязычного текста, чем Windows-1251, которая использует лишь 1 байт.
В качестве решения можно записать всю последовательность подряд (надеюсь без ошибок): «11010000 10011111 11010001 10000000 11010000 10111000 11010000 10110010 11010000 10110101 11010001 10000010 00100000 01001000 01101001».
Проверить решение можно кодом:
Оптимизированный PHP код, который позволяет получать числовое представление символов и обратную операцию (полную версию опубликую в конце цикла):
Метод getChar() был взят с библиотеки Jevix, я всё-равно уже видел этот код, хорошо его запомнил и даже при его реализации по памяти было бы нечестно не упомянуть автора.
Вы же можете протестировать получившийся класс при помощи кода:
Я не старался писать самый красивый или правильный код для тестов, но при помощи него вы можете спокойно побитово менять значения символов и сразу видеть результат. Все невалидные последовательности будут проигнорированы, выводимая строка всегда валидна, но это ещё далеко не всё.
Чтобы быть уверенным, что текст не содержит ничего лишнего нужно удалить с него ненужные (непечатные, нарушающие разметку, неопределённые, суррогатные и т.п.) символы и провести нормализацию, об этом в следующей части.
Дальше будет про нормализацию, безопасность, определение кодировок и работу с UTF-8 в PHP.
utf8_encode
(PHP 4, PHP 5, PHP 7, PHP 8)
utf8_encode — Кодирует строку ISO-8859-1 в кодировке UTF-8
Описание
Эта функция конвертирует строку string из кодировки ISO-8859-1 в UTF-8
Список параметров
Возвращаемые значения
Список изменений
Версия | Описание |
---|---|
7.2.0 | Функция была перенесена в ядро PHP, таким образом отменив требование модуля XML для использования этой функции. |
Смотрите также
User Contributed Notes 23 notes
Please note that utf8_encode only converts a string encoded in ISO-8859-1 to UTF-8. A more appropriate name for it would be «iso88591_to_utf8». If your text is not encoded in ISO-8859-1, you do not need this function. If your text is already in UTF-8, you do not need this function. In fact, applying this function to text that is not encoded in ISO-8859-1 will most likely simply garble that text.
If you need to convert text from any encoding to any other encoding, look at iconv() instead.
Here’s some code that addresses the issue that Steven describes in the previous comment;
/* This structure encodes the difference between ISO-8859-1 and Windows-1252,
as a map from the UTF-8 encoding of some ISO-8859-1 control characters to
the UTF-8 encoding of the non-control characters that Windows-1252 places
at the equivalent code points. */
Walk through nested arrays/objects and utf8 encode all strings.
If you need a function which converts a string array into a utf8 encoded string array then this function might be useful for you:
My version of utf8_encode_deep,
In case you need one that returns a value without changing the original.
I tried a lot of things, but this seems to be the final fail save method to convert any string to proper UTF-8.
If your string to be converted to utf-8 is something other than iso-8859-1 (such as iso-8859-2 (Polish/Croatian)), you should use recode_string() or iconv() instead rather than trying to devise complex str_replace statements.
If you are looking for a function to replace special characters with the hex-utf-8 value (e.g. für Webservice-Security/WSS4J compliancy) you might use this:
$textstart = «Größe»;
$utf8 =»;
$max = strlen($txt);
I was searching for a function similar to Javascript’s unescape(). In most cases it is OK to use url_decode() function but not if you’ve got UTF characters in the strings. They are converted into %uXXXX entities that url_decode() cannot handle.
I googled the net and found a function which actualy converts these entities into HTML entities (&#xxx;) that your browser can show correctly. If you’re OK with that, the function can be found here: http://pure-essence.net/stuff/code/utf8RawUrlDecode.phps
But it was not OK with me because I needed a string in my charset to make some comparations and other stuff. So I have modified the above function and in conjuction with code2utf() function mentioned in some other note here, I have managed to achieve my goal:
// Validate Unicode UTF-8 Version 4
// This function takes as reference the table 3.6 found at http://www.unicode.org/versions/Unicode4.0.0/ch03.pdf
// It also flags overlong bytes as error
This function may be useful do encode array keys and values [and checks first to see if it’s already in UTF format]:
[NOTE BY danbrown AT php DOT net: Original function written by (cmyk777 AT gmail DOT com) on 28-JAN-09.]
Avoiding use of preg_match to detect if utf8_encode is needed:
I recommend using this alternative for every language:
Don’t forget to set all your pages to «utf-8» encoding, otherwise just use HTML entities.
This function I use convert Thai font (iso-8859-11) to UTF-8. For my case, It work properly. Please try to use this function if you have a problem to convert charset iso-8859-11 to UTF-8.
$iso8859_11 = array(
«\xa1» => «\xe0\xb8\x81»,
«\xa2» => «\xe0\xb8\x82»,
«\xa3» => «\xe0\xb8\x83»,
«\xa4» => «\xe0\xb8\x84»,
«\xa5» => «\xe0\xb8\x85»,
«\xa6» => «\xe0\xb8\x86»,
«\xa7» => «\xe0\xb8\x87»,
«\xa8» => «\xe0\xb8\x88»,
«\xa9» => «\xe0\xb8\x89»,
«\xaa» => «\xe0\xb8\x8a»,
«\xab» => «\xe0\xb8\x8b»,
«\xac» => «\xe0\xb8\x8c»,
«\xad» => «\xe0\xb8\x8d»,
«\xae» => «\xe0\xb8\x8e»,
«\xaf» => «\xe0\xb8\x8f»,
«\xb0» => «\xe0\xb8\x90»,
«\xb1» => «\xe0\xb8\x91»,
«\xb2» => «\xe0\xb8\x92»,
«\xb3» => «\xe0\xb8\x93»,
«\xb4» => «\xe0\xb8\x94»,
«\xb5» => «\xe0\xb8\x95»,
«\xb6» => «\xe0\xb8\x96»,
«\xb7» => «\xe0\xb8\x97»,
«\xb8» => «\xe0\xb8\x98»,
«\xb9» => «\xe0\xb8\x99»,
«\xba» => «\xe0\xb8\x9a»,
«\xbb» => «\xe0\xb8\x9b»,
«\xbc» => «\xe0\xb8\x9c»,
«\xbd» => «\xe0\xb8\x9d»,
«\xbe» => «\xe0\xb8\x9e»,
«\xbf» => «\xe0\xb8\x9f»,
«\xc0» => «\xe0\xb8\xa0»,
«\xc1» => «\xe0\xb8\xa1»,
«\xc2» => «\xe0\xb8\xa2»,
«\xc3» => «\xe0\xb8\xa3»,
«\xc4» => «\xe0\xb8\xa4»,
«\xc5» => «\xe0\xb8\xa5»,
«\xc6» => «\xe0\xb8\xa6»,
«\xc7» => «\xe0\xb8\xa7»,
«\xc8» => «\xe0\xb8\xa8»,
«\xc9» => «\xe0\xb8\xa9»,
«\xca» => «\xe0\xb8\xaa»,
«\xcb» => «\xe0\xb8\xab»,
«\xcc» => «\xe0\xb8\xac»,
«\xcd» => «\xe0\xb8\xad»,
«\xce» => «\xe0\xb8\xae»,
«\xcf» => «\xe0\xb8\xaf»,
«\xd0» => «\xe0\xb8\xb0»,
«\xd1» => «\xe0\xb8\xb1»,
«\xd2» => «\xe0\xb8\xb2»,
«\xd3» => «\xe0\xb8\xb3»,
«\xd4» => «\xe0\xb8\xb4»,
«\xd5» => «\xe0\xb8\xb5»,
«\xd6» => «\xe0\xb8\xb6»,
«\xd7» => «\xe0\xb8\xb7»,
«\xd8» => «\xe0\xb8\xb8»,
«\xd9» => «\xe0\xb8\xb9»,
«\xda» => «\xe0\xb8\xba»,
«\xdf» => «\xe0\xb8\xbf»,
«\xe0» => «\xe0\xb9\x80»,
«\xe1» => «\xe0\xb9\x81»,
«\xe2» => «\xe0\xb9\x82»,
«\xe3» => «\xe0\xb9\x83»,
«\xe4» => «\xe0\xb9\x84»,
«\xe5» => «\xe0\xb9\x85»,
«\xe6» => «\xe0\xb9\x86»,
«\xe7» => «\xe0\xb9\x87»,
«\xe8» => «\xe0\xb9\x88»,
«\xe9» => «\xe0\xb9\x89»,
«\xea» => «\xe0\xb9\x8a»,
«\xeb» => «\xe0\xb9\x8b»,
«\xec» => «\xe0\xb9\x8c»,
«\xed» => «\xe0\xb9\x8d»,
«\xee» => «\xe0\xb9\x8e»,
«\xef» => «\xe0\xb9\x8f»,
«\xf0» => «\xe0\xb9\x90»,
«\xf1» => «\xe0\xb9\x91»,
«\xf2» => «\xe0\xb9\x92»,
«\xf3» => «\xe0\xb9\x93»,
«\xf4» => «\xe0\xb9\x94»,
«\xf5» => «\xe0\xb9\x95»,
«\xf6» => «\xe0\xb9\x96»,
«\xf7» => «\xe0\xb9\x97»,
«\xf8» => «\xe0\xb9\x98»,
«\xf9» => «\xe0\xb9\x99»,
«\xfa» => «\xe0\xb9\x9a»,
«\xfb» => «\xe0\xb9\x9b»
);
// Reads a file story.txt ascii (as typed on keyboard)
// converts it to Georgian character using utf8 encoding
// if I am correct(?) just as it should be when typed on Georgian computer
// it outputs it as an html file
//
// http://www.comweb.nl/keys_to_georgian.html
// http://www.comweb.nl/keys_to_georgian.php
// http://www.comweb.nl/story.txt
keys to unicode code
// this meta tag is needed
// note the sylfean font seems to be standard installed on Windows XP
// It supports Georgian
Re the previous post about converting GB2312 code to Unicode code which displayed the following function:
In the original function, the first latin chacter was dropped and it was not converting the first non-latin character after the latin text (everything was shifted one character too far to the right). Reversing those two lines makes it work correctly in every example I have tried.
Also, the source of the gb2312.txt file needed for this to work has changed. You can find it a couple places:
Someday they might be hardcoded into PHP.
*/
The following Perl regular expression tests if a string is well-formed Unicode UTF-8 (Broken up after each | since long lines are not permitted here. Please join as a single line, no spaces, before use.):
Определение кодировки текста в PHP — обзор существующих решений плюс еще один велосипед
Столкнулся с задачей — автоопределение кодировки страницы/текста/чего угодно. Задача не нова, и велосипедов понапридумано уже много. В статье небольшой обзор найденного в сети — плюс предложение своего, как мне кажется, достойного решения.
Если кратко — он не работает.
Давайте смотреть:
Как видим, на выходе — полная каша. Что мы делаем, когда непонятно почему так себя ведет функция? Правильно, гуглим. Нашел замечательный ответ.
Чтобы окончательно развеять все надежды на использование mb_detect_encoding(), надо залезть в исходники расширения mbstring. Итак, закатали рукава, поехали:
Постить полный текст метода не буду, чтобы не засорять статью лишними исходниками. Кому это интересно посмотрят сами. Нас истересует строка под номером 593, где собственно и происходит проверка того, подходит ли символ под кодировку:
Вот основные фильтры для однобайтовой кириллицы:
Windows-1251 (оригинальные комментарии сохранены)
ISO-8859-5 (тут вообще все весело)
Как видим, ISO-8859-5 всегда возвращает TRUE (чтобы вернуть FALSE, нужно выставить filter->flag = 1).
Когда посмотрели фильтры, все встало на свои места. CP1251 от KOI8-R не отличить никак. ISO-8859-5 вообще если есть в списке кодировок — будет всегда детектиться как верная.
В общем, fail. Оно и понятно — только по кодам символов нельзя в общем случае узнать кодировку, так как эти коды пересекаются в разных кодировках.
2. Что выдает гугл
А гугл выдает всякие убожества. Даже не буду постить сюда исходники, сами посмотрите, если захотите (уберите пробел после http://, не знаю я как показать текст не ссылкой):
http:// deer.org.ua/2009/10/06/1/
http:// php.su/forum/topic.php?forum=1&topic=1346
3. Поиск по хабру
2) на мой взгляд, очень интересное решение: habrahabr.ru/blogs/php/27378/#comment_1399654
Минусы и плюсы в комменте по ссылке. Лично я считаю, что только для детекта кодировки это решение избыточно — слишком мощно получается. Определение кодировки в нем — как побочный эффект ).
4. Собственно, мое решение
Идея возникла во время просмотра второй ссылки из прошлого раздела. Идея следующая: берем большой русский текст, замеряем частоты разных букв, по этим частотам детектим кодировку. Забегая вперед, сразу скажу — будут проблемы с большими и маленькими буквами. Поэтому выкладываю примеры частот букв (назовем это — «спектр») как с учетом регистра, так и без (во втором случае к маленькой букве добавлял еще большую с такой же частотой, а большие все удалял). В этих «спектрах» вырезаны все буквы, имеющие частоты меньше 0,001 и пробел. Вот, что у меня получилось после обработки «Войны и Мира»:
Спектры в разных кодировках (ключи массива — коды соответствующих символов в соответствующей кодировке):
Далее. Берем текст неизвестной кодировки, для каждой проверяемой кодировки находим частоту текущего символа и прибавляем к «рейтингу» этой кодировки. Кодировка с бОльшим рейтингом и есть, скорее всего, кодировка текста.
Результаты
У-упс! Полная каша. А потому что большие буквы в CP1251 обычно соответствуют маленьким в KOI8-R. А маленькие буквы используются в свою очередь намного чаще, чем большие. Вот и определяем строку капсом в CP1251 как KOI8-R.
Пробуем делать без учета регистра («спектры» case insensitive)
Как видим, верная кодировка стабильно лидирует и с регистрозависимыми «спектрами» (если строка содержит небольшое количество заглавных букв), и с регистронезависимыми. Во втором случае, с регистронезависимыми, лидирует не так уверенно, конечно, но вполне стабильно даже на маленьких строках. Можно поиграться еще с весами букв — сделать их нелинейными относительно частоты, например.
5. Заключение
В топике не расмотрена работа с UTF-8 — тут никакий принципиальной разницы нету, разве что получение кодов символов и разбиение строки на символы будет несколько длиннее/сложнее.
Эти идеи можно распространить не только на кириллические кодировки, конечно — вопрос только в «спектрах» соответствующих языков/кодировок.
P.S. Если будет очень нужно/интересно — потом выложу второй частью полностью работающую библиотеку на GitHub. Хотя я считаю, что данных в посте вполне достаточно для быстрого написания такой библиотеки и самому под свои нужды — «спектр» для русского языка выложен, его можно без труда перенести на все нужные кодировки.
UPDATED
В комментариях проскочила замечательная функция, ссылку на которую я опубликовал под графом «убожество». Может быть погорячился со словами, но уж как опубликовал, так опубликовал — редактировать такие вещи не привык. Чтобы не быть голословным, давайте разберемся, работает ли она на 100%, как об этом говорит предполагаемый автор.
1) будут ли ошибки при «нормальной» работе этой функции? Предположим, что контент у нас на 100% валидный.
ответ: да, будут.
2) определит ли она что-нибудь кроме UTF-8 и не-UTF-8?
ответ: нет, не определит.
Настройка локали в php под windows для работы с UTF-8?
Создаём файл index.php в кодировке UTF-8 без BOM c таким содержанием:
И запускаем скрипт под Windows.
Я получаю следующий вывод:
Число: 19, месяц: ������, день недели: �����������
Иногда слетает на такое:
Число: 19, месяц: November, день недели: Monday
Принудительное выставление локали
так же ничего не меняет.
Кто-то может подсказать как решить сию проблему?
Обязательно требование сохранить кодировку у файлов UTF-8 и заставить Windows нормально с ними работать. Это реально?
Замечу так же что если убрать функцию setlocale() из приведенного выше кода, то изредка локаль вообще слетает на английскую и текст получается следующий:
Число: 19, месяц: November, день недели: Monday
Чем это может быть обосновано?
Говорят под Windows strftime кладёт на выбранную кодировку в локали и работает в однобайтовой кодировке, соответствующей запрошенному языку.
Можно, разве что, перед каждыми подобными функциями (переключив на всякий случай локаль на знакомую винде), перекодировать ввод функции в однобайтную кодировку, вызывать ей, после чего перекодировать результат в utf-8.