Вопрос по utf-8, character-encoding, iconv – Принудительное кодирование из US-ASCII в UTF-8 (iconv)

51

Я пытаюсь перекодировать несколько файлов из US-ASCII в UTF-8.

Для этого я использую iconv:

iconv -f US-ASCII -t UTF-8 file.php > file-utf8.php

Дело в том, что мои оригинальные файлы имеют кодировку US-ASCII, что делает преобразование невозможным. Очевидно, это происходит потому, что ASCII является подмножеством UTF-8 ...

http://www.linuxquestions.org/questions/linux-software-2/iconv-us-ascii-to-utf-8-or-iso-8859-15-a-705054/

И цитирую:

There's no need for the textfile to appear otherwise until non-ascii characters are introduced

Правда. Если я введу не-ASCII-символ в файл и сохраню его, скажем, с помощью Eclipse, кодировка файла (кодировка) переключится на UTF-8.

В моем случае я бы хотелforce iconv to transcode the files to UTF-8 anyway, Есть ли в нем не-ASCII символы или нет.

Примечание: причина в том, что мой PHP-код (не ASCII-файлы ...) имеет дело с некоторой не-ASCII-строкой, что приводит к тому, что строки плохо интерпретируются (по-французски):

Il était une fois... l'homme série animée mythique d'Albert

Barillé (Procidis), 1ère

...

EDIT

US-ASCII -- is -- a subset of UTF-8 (see Ned's answer below) Meaning that US-ASCII files are actually encoded in UTF-8 My problem came from somewhere else
Вы можете вспомнить, откуда возникла ваша проблема? У меня похожая проблема DrogoNevets
@DrogoNevets Точно не помню, но я думаю, что это связано с работой с UTF8 в PHP и в / из БД ...utf8_encode, utf8_decodeи т.д ... Или более подробно:toptal.com/php/a-utf-8-primer-for-php-and-mysql stackoverflow.com/questions/279170/utf-8-all-the-way-through eightyfive
Чтобы сделать обратное (UTF8 до ASCII), см.How to remove accents and turn letters into “plain” ASCII characters?. Skippy le Grand Gourou

Ваш Ответ

8   ответов
34
Short Answer file only guesses at the file encoding and may be wrong (especially in cases where special characters only appear late in large files). you can use hexdump to look at bytes of non-7-bit-ascii text and compare against code tables for common encodings (iso-8859-*, utf-8) to decide for yourself what the encoding is. iconv will use whatever input/output encoding you specify regardless of what the contents of the file are. If you specify the wrong input encoding the output will be garbled. even after running iconv, file may not report any change due to the limited way in which file attempts to guess at the encoding. For a specific example, see my long answer. 7-bit ascii (aka us-ascii) is identical at a byte level to utf-8 and the 8-bit ascii extensions (iso-8859-*). So if your file only has 7-bit characters, then you can call it utf-8, iso-8859-* or us-ascii because at a byte level they are all identical. It only makes sense to talk about utf-8 and other encodings (in this context) once your file has characters outside the 7-bit ascii range. Long Answer

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

Во-первых, термин ASCII перегружен, что приводит к путанице.

7-битный ASCII включает в себя только 128 символов (00-7F или 0-127 в десятичном формате). 7-битный ASCII также называется US-ASCII.

https://en.wikipedia.org/wiki/ASCII

Кодирование UTF-8 использует ту же кодировку, что и 7-битный ASCII для своих первых 128 символов. Таким образом, текстовый файл, который содержит только символы из этого диапазона первых 128 символов, будет идентичен на уровне байтов, независимо от того, закодирован ли он в UTF-8 или 7-битном ASCII.

https://en.wikipedia.org/wiki/UTF-8#Codepage_layout

The term extended ascii (or high ascii) refers to eight-bit or larger character encodings that include the standard seven-bit ASCII characters, plus additional characters.

https://en.wikipedia.org/wiki/Extended_ASCII

ISO-8859-1 (он же «ISO Latin 1») является специальным 8-битным стандартом расширения ASCII, который охватывает большинство символов для Западной Европы. Существуют и другие стандарты ISO для языков Восточной Европы и кириллицы. ISO-8859-1 включает такие символы, как & # xD6 ;, & # xE9 ;, & # xF1; и & # xDF; для немецкого и испанского языков. & Quot; Удлинитель & Quot; означает, что ISO-8859-1 включает 7-битный стандарт ASCII и добавляет к нему символы, используя 8-й бит. Таким образом, для первых 128 символов он эквивалентен на уровне байтов файлам в кодировке ASCII и UTF-8. Однако, когда вы начинаете работать с символами после первых 128, вы больше не эквивалентны UTF-8 на уровне байтов, и вы должны выполнить преобразование, если вы хотите, чтобы ваш "расширенный ascii" был изменен. файл в кодировке UTF-8.

https://en.wikipedia.org/wiki/Extended_ASCII#ISO_8859_and_proprietary_adaptations

Один урок, который я усвоил сегодня, заключается в том, что мы не можем доверятьfile всегда давать правильную интерпретацию кодировки символов файла.

https://en.wikipedia.org/wiki/File_%28command%29

The command tells only what the file looks like, not what it is (in the case where file looks at the content). It is easy to fool the program by putting a magic number into a file the content of which does not match it. Thus the command is not usable as a security tool other than in specific situations.

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

Мой файл - большой файл CSV.file сообщает об этом файле в кодировке us-ascii, чтоWRONG.

$ ls -lh
total 850832
-rw-r--r--  1 mattp  staff   415M Mar 14 16:38 source-file
$ file -b --mime-type source-file
text/plain
$ file -b --mime-encoding source-file
us-ascii

В моем файле есть умляуты (т.е. & # xD6;). Первая не-7-битная ascii не отображается до тех пор, пока в файл не попадает более 100 тыс. Строк. Я подозреваю, что именно поэтомуfile не понимает, что кодировка файла не является US-ASCII.

$ pcregrep -no '[^\x00-\x7F]' source-file | head -n1
102321:�

Я на Mac, поэтому с помощью PCREgrep, С GNU grep вы можете использовать-P вариант. В качестве альтернативы на Mac можно установить coreutils (через homebrew или другое), чтобы получить GNU grep.

Я не копался в исходном кодеfileи страница man не обсуждает детальное обнаружение кодировки текста, но я предполагаюfile не просматривает весь файл, прежде чем угадать кодировку.

Какой бы ни была кодировка моего файла, эти не-7-битные символы ASCII нарушают работу. Мой немецкий CSV-файл;-разделенный и извлекающий один столбец не работает.

$ cut -d";" -f1 source-file > tmp
cut: stdin: Illegal byte sequence
$ wc -l *
 3081673 source-file
  102320 tmp
 3183993 total

Обратите вниманиеcut ошибка и что мой & quot; tmp & quot; Файл содержит всего 102320 строк с первым специальным символом в строке 102321.

Давайте посмотрим, как кодируются эти символы, не входящие в ASCII. Я сбрасываю первый не 7-битный ASCII вhexdump, сделайте небольшое форматирование, удалите переводы строки (0a) и возьмите только первые несколько.

$ pcregrep -o '[^\x00-\x7F]' source-file | head -n1 | hexdump -v -e '1/1 "%02x\n"'
d6
0a

По-другому. Я знаю, что первый не 7-битный ASCII-символ находится в позиции 85 в строке 102321. Я беру эту строку и говорюhexdump взять два байта, начиная с позиции 85. Вы можете увидеть специальный (не 7-битный ASCII) символ, представленный символом ".", а следующий байт - "M" ... так что это однобайтовая кодировка символов.

$ tail -n +102321 source-file | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

В обоих случаях мы видим, что специальный символ представленd6, Поскольку этот символ является & # xD6; это немецкое письмо, я предполагаю, что ISO-8859-1 должен включать это. Конечно же, вы можете увидеть "d6" это совпадение (https://en.wikipedia.org/wiki/ISO/IEC_8859-1#Codepage_layout).

Важный вопрос ... откуда мне знать, что этот символ - & # xD6; не будучи уверенным в кодировке файла? Ответ является контекстом. Я открыл файл, прочитал текст и затем определил, каким символом он должен быть. Если я открою это вvim отображается как & # xD6; так какvim делает лучшую работуguessing кодировка символов (в данном случае) чемfile делает.

Итак, мой файл выглядит как ISO-8859-1. Теоретически я должен проверить остальные символы, не входящие в 7-битный ASCII, чтобы убедиться, что ISO-8859-1 хорошо подходит ... Нет ничего, что заставляло бы программу использовать только одну кодировку при записи файла в диск (кроме хороших манер).

Я пропущу проверку и перейду к этапу конверсии.

$ iconv -f iso-8859-1 -t utf8 source-file > output-file
$ file -b --mime-encoding output-file
us-ascii

Хм.file все еще говорит мне, что этот файл является US-ASCII даже после преобразования. Давайте проверим сhexdump снова.

$ tail -n +102321 output-file | head -n1 | hexdump -C -s85 -n2
00000055  c3 96                                             |..|
00000057

Определенно изменение. Обратите внимание, что у нас есть два байта не-7-битного ASCII (представленного «.» Справа), и шестнадцатеричный код для этих двух байтов теперьc3 96, Если мы посмотрим, кажется, у нас сейчас есть UTF-8 (c3 96 - правильная кодировка & # xD6; в UTF-8)http://www.utf8-chartable.de/

Ноfile все еще сообщает наш файл какus-ascii? Ну, я думаю, что это восходит к вопросу оfile не глядя на весь файл и на тот факт, что первые не-7-битные символы ASCII не появляются до глубины файла.

Я используюsed прикрепить & # xD6; в начале файла и посмотрим, что произойдет.

$ sed '1s/^/Ö\'

Круто, у нас умлаут. Обратите внимание, что кодировка c3 96 (utf-8). Хм.

Снова проверяем наши другие умлауты в том же файле:

$ tail -n +102322 test-file | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

ISO-8859-1. К сожалению! Просто показывает, как легко облажаться кодировки.

Давайте попробуем преобразовать наш новый тестовый файл с умлаутом впереди и посмотрим, что произойдет.

$ iconv -f iso-8859-1 -t utf8 test-file > test-file-converted
$ head -n1 test-file-converted | hexdump -C
00000000  c3 83 c2 96 0a                                    |.....|
00000005
$ tail -n +102322 test-file-converted | head -n1 | hexdump -C -s85 -n2
00000055  c3 96                                             |..|
00000057

К сожалению. Тот первый умлаут, который был UTF-8, был интерпретирован как ISO-8859-1, так как это то, что мы сказалиiconv, Второй умлаут правильно конвертируется изd6 вc3 96.

Я попробую еще раз, на этот раз я буду использоватьvim сделать & # xD6; вставка вместоsed. vim казалось, что кодирование лучше обнаруживается (как «latin1» или «ISO-8859-1»), поэтому, возможно, он вставит новый & # xD6; с последовательной кодировкой.

$ vim source-file
$ head -n1 test-file-2
�
$ head -n1 test-file-2 | hexdump -C
00000000  d6 0d 0a                                          |...|
00000003
$ tail -n +102322 test-file-2 | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

Выглядит хорошо. Похоже на ISO-8859-1 для новых и старых умлаутов.

Сейчас тест.

$ file -b --mime-encoding test-file-2
iso-8859-1
$ iconv -f iso-8859-1 -t utf8 test-file-2 > test-file-2-converted
$ file -b --mime-encoding test-file-2-converted
utf-8

Boom! Мораль истории. Не доверяйfile чтобы всегда угадывать вашу кодировку правильно. Легко смешивать кодировки в одном файле. Если сомневаетесь, посмотрите на гекс.

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

$ first_special=$(pcregrep -o1 -n '()[^\x00-\x7F]' source-file | head -n1 | cut -d":" -f1)
$ tail -n +$first_special source-file > /tmp/source-file-shorter
$ file -b --mime-encoding /tmp/source-file-shorter
iso-8859-1
Update

Христос Зулас обновленfile чтобы количество байтов выглядело настраиваемым. Один день на запрос функции, круто!

http://bugs.gw.com/view.php?id=533 https://github.com/file/file/commit/d04de269e0b06ccd0a7d1bf4974fed1d75be7d9e

Функция была выпущена вfile версия 5.26.

Глядя на большие файлы, прежде чем делать предположения о кодировании, требуется время. Тем не менее, было бы неплохо иметь возможность для конкретных случаев использования, когда лучшие предположения могут перевесить дополнительное время / io.

Используйте следующую опцию:

−P, −−parameter name=value

    Set various parameter limits.

    Name    Default     Explanation
    bytes   1048576     max number of bytes to read from file

Что-то вроде...

file_to_check="myfile"
bytes_to_scan=$(wc -c < $file_to_check)
file -b --mime-encoding -P bytes=$bytes_to_scan $file_to_check

... должен сделать свое дело, если вы хотите, чтобы заставитьfile чтобы просмотреть весь файл, прежде чем делать предположение. Конечно, это работает только если у вас естьfile 5.26 или новее.

Я еще не собрал / не протестировал последние версии. Большинство моих машин в настоящее время имеютfile 5.04 (2010) ... надеюсь, когда-нибудь этот релиз выйдет из апстрима.

\n/' source-file > test-file $ head -n1 test-file Ö $ head -n1 test-file | hexdump -C 00000000 c3 96 0a |...| 00000003

Круто, у нас умлаут. Обратите внимание, что кодировка c3 96 (utf-8). Хм.

Снова проверяем наши другие умлауты в том же файле:

$ tail -n +102322 test-file | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

ISO-8859-1. К сожалению! Просто показывает, как легко облажаться кодировки.

Давайте попробуем преобразовать наш новый тестовый файл с умлаутом впереди и посмотрим, что произойдет.

$ iconv -f iso-8859-1 -t utf8 test-file > test-file-converted
$ head -n1 test-file-converted | hexdump -C
00000000  c3 83 c2 96 0a                                    |.....|
00000005
$ tail -n +102322 test-file-converted | head -n1 | hexdump -C -s85 -n2
00000055  c3 96                                             |..|
00000057

К сожалению. Тот первый умлаут, который был UTF-8, был интерпретирован как ISO-8859-1, так как это то, что мы сказалиiconv, Второй умлаут правильно конвертируется изd6 вc3 96.

Я попробую еще раз, на этот раз я буду использоватьvim сделать & # xD6; вставка вместоsed. vim казалось, что кодирование лучше обнаруживается (как «latin1» или «ISO-8859-1»), поэтому, возможно, он вставит новый & # xD6; с последовательной кодировкой.

$ vim source-file
$ head -n1 test-file-2
�
$ head -n1 test-file-2 | hexdump -C
00000000  d6 0d 0a                                          |...|
00000003
$ tail -n +102322 test-file-2 | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

Выглядит хорошо. Похоже на ISO-8859-1 для новых и старых умлаутов.

Сейчас тест.

$ file -b --mime-encoding test-file-2
iso-8859-1
$ iconv -f iso-8859-1 -t utf8 test-file-2 > test-file-2-converted
$ file -b --mime-encoding test-file-2-converted
utf-8

Boom! Мораль истории. Не доверяйfile чтобы всегда угадывать вашу кодировку правильно. Легко смешивать кодировки в одном файле. Если сомневаетесь, посмотрите на гекс.

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

$ first_special=$(pcregrep -o1 -n '()[^\x00-\x7F]' source-file | head -n1 | cut -d":" -f1)
$ tail -n +$first_special source-file > /tmp/source-file-shorter
$ file -b --mime-encoding /tmp/source-file-shorter
iso-8859-1
Update

Христос Зулас обновленfile чтобы количество байтов выглядело настраиваемым. Один день на запрос функции, круто!

http://bugs.gw.com/view.php?id=533 https://github.com/file/file/commit/d04de269e0b06ccd0a7d1bf4974fed1d75be7d9e

Функция была выпущена вfile версия 5.26.

Глядя на большие файлы, прежде чем делать предположения о кодировании, требуется время. Тем не менее, было бы неплохо иметь возможность для конкретных случаев использования, когда лучшие предположения могут перевесить дополнительное время / io.

Используйте следующую опцию:

−P, −−parameter name=value

    Set various parameter limits.

    Name    Default     Explanation
    bytes   1048576     max number of bytes to read from file

Что-то вроде...

file_to_check="myfile"
bytes_to_scan=$(wc -c < $file_to_check)
file -b --mime-encoding -P bytes=$bytes_to_scan $file_to_check

... должен сделать свое дело, если вы хотите, чтобы заставитьfile чтобы просмотреть весь файл, прежде чем делать предположение. Конечно, это работает только если у вас естьfile 5.26 или новее.

Я еще не собрал / не протестировал последние версии. Большинство моих машин в настоящее время имеютfile 5.04 (2010) ... надеюсь, когда-нибудь этот релиз выйдет из апстрима.

Отличное объяснение. Это должен быть главный ответ. У меня есть точный сценарий, который вы описали здесь.
Спасибо за ваш отзыв, я обновил свой ответ, чтобы попытаться быть более полезным. ;)
В самом деле,file только просматривает первые несколько килобайт файла, чтобы получить его вердикт.
Я добавил недостающие ссылки, хотя я не был уверен, правильно ли я угадал последнюю.
(Соблазн также исправить & lt;, href = "http: //www.iki.fi/era/unix/award.html" rel =" nofollow noreferrer "& gt; бесполезныйcat но я оставлю это себе.)
15

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

Если вы действительно хотите, чтобы он отображался в utf-8 вместо us-ascii, то вам нужно сделать это в 2 шага.

первый :

iconv -f us-ascii -t utf-16 yourfile > youfileinutf16.*

второй:

iconv -f utf-16le -t utf-8 yourfileinutf16 > yourfileinutf8.*

затем, если вы сделаете файл -i, вы увидите, что новая кодировка - это utf-8.

Надеюсь, поможет.

57

ASCII является подмножеством UTF-8, поэтому все файлы ASCII уже имеют кодировку UTF-8. Байты в файле ASCII и байты, которые могут возникнуть в результате «кодирования его в UTF-8» были бы точно такие же байты. Между ними нет никакой разницы, поэтому не нужно ничего делать.

Похоже, ваша проблема в том, что файлы на самом деле не являются ASCII. Вам необходимо определить, какую кодировку они используют, и правильно их перекодировать.

2

Вот сценарий, который найдет все файлы, соответствующие шаблону, который вы передаете, и затем преобразует их из текущей кодировки в utf-8. Если кодировка us-ascii, то она все равно будет отображаться как us-ascii, так как это подмножество utf-8.

#!/usr/bin/env bash    
find . -name "${1}" |
    while read line;
    do
        echo "***************************"
        echo "Converting ${line}"

        encoding=$(file -b --mime-encoding ${line}) 
        echo "Found Encoding: ${encoding}"

        iconv -f "${encoding}" -t "utf-8" ${line} -o ${line}.tmp
        mv ${line}.tmp ${line}
    done
1

Я случайно закодировал файл в UTF-7 и у меня была похожая проблема. Когда я набралfile -i name.file я бы получилcharset=us-ascii. iconv -f us-ascii -t utf-9//translit name.file не будет работать, так как я собрал UTF-7, как и UTF-8, подмножество us-ascii.

Чтобы решить эту проблему, я ввел:iconv -f UTF-7 -t UTF-8//TRANSLIT name.file -o output.file

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

11

Я думаюНед имеет суть проблемы - ваши файлы на самом деле не являются ASCII. Пытаться

iconv -f ISO-8859-1 -t UTF-8 file.php > file-utf8.php

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

file не проверять весь файл; попробуйте переместить строки в начало файла, возможно, в блоке комментариев.
Еще один способ проверить, есть ли у вас файл ascii, - запустить скрипт, подобный этой Ruby-программе:File.open("file.php").each_char {|c| puts c if c.ord > 127}, (Я выбрал Ruby, потому что я знал, как написать это быстро; любой другой подобный язык был бы так же легок.)
Согласно Smultron, мои файлы в кодировке Unicode (UTF-8) ... Так что Нед действительно прав. US-ASCII является подмножеством UTF-8. Тогда моя проблема должна исходить из чего-то другого (дело в том, что я не имею дело со строками, не относящимися к ASCII, внутри php-файла, НО я получаю их через Интернет: я очищаю веб-страницу ...). Спасибо за ваше время! eightyfive
Нету. Это не помогло. Я попробовал, но все равно, если я бегу$ file --mime file.php я получилfile.php: text/x-php charset=us-ascii... Итак, я предполагаю, что мои файлы на самом деле в кодировке ASCII? eightyfive
2

Нет никакой разницы между US-ASCII и UTF-8, поэтому нет необходимости переконвертировать его. Но здесь небольшой намек, если у вас возникли проблемы со специальными символами при перекодировании.

Добавьте // TRANSLIT после параметра source-charset-Parameter.

Example:

iconv -f ISO-8859-1//TRANSLIT -t UTF-8 filename.sql > utf8-filename.sql

Это помогает мне в странных типах кавычек, которые всегда нарушают процесс перекодирования кодировки.

1

Ты можешь использоватьfile -i file_name чтобы проверить, что именно ваш оригинальный формат файла.

Как только вы это получите, вы можете сделать следующее:

iconv -f old_format -t utf-8 input_file -o output_file

Похожие вопросы