Подтвердить что ты не робот

Сила кодируется из US-ASCII в UTF-8 (iconv)

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

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

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

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

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

И цитирование:

Нет необходимости, чтобы текстовый файл появлялся иначе, пока не-ascii вводятся символы

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

В моем случае я хотел бы заставить iconv перекодировать файлы на UTF-8 в любом случае. Есть ли в нем символы, отличные от ASCII, или нет.

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

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

Barillà © (Procidis), 1ère

...

ИЗМЕНИТЬ

  • US-ASCII - - подмножество UTF-8 (см. ответ Ned ниже)
  • Значение US-ASCII файлов действительно закодировано в UTF-8
  • Моя проблема возникла где-то в другом месте
4b9b3361

Ответ 1

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

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

Ответ 2

Короткий ответ

  • file только догадывается о кодировке файла и может быть неправильным.
  • вы можете использовать hexdump для просмотра байтов текста без 7-бит-ascii и сравнения с кодовыми таблицами для общих кодировок (7-бит-ascii, iso-8859- *, utf-8), чтобы решить что такое кодировка.
  • iconv будет использовать любую кодировку ввода/вывода, которую вы укажете вне зависимости от содержимого файла. Если вы укажете неправильную кодировку ввода, выход будет искажен.
  • даже после запуска iconv, file может не сообщать о каких-либо изменениях из-за ограниченного способа, с помощью которого file пытается угадать кодировку. Для конкретного примера см. Мой длинный ответ.

Длинный ответ

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

Во-первых, термин 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

Термин расширенный ascii (или высокий ascii) относится к восьмибитным или большему кодированию символов, которые включают стандартные семибитные символы ASCII, плюс дополнительные символы.

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

ISO-8859-1 (он же "ISO Latin 1" ) - это специальный 8-разрядный стандарт расширения ASCII, который охватывает большинство символов для Западной Европы. Существуют и другие стандарты ИСО для восточноевропейских языков и кириллических языков. ISO-8859-1 включает такие символы, как Ö, é, ñ и ß для немецкого и испанского языков. "Расширение" означает, что 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

Команда сообщает только, как выглядит файл, а не то, что он (в случае, когда файл просматривает содержимое). Легко обмануть программу, поместив магическое число в файл, содержимое которого не соответствует ему. Таким образом, команда не используется в качестве инструмента безопасности, кроме как в определенных ситуациях.

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

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

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

(Я нахожусь на Mac, поэтому использую PCRE grep. С gnu grep вы можете использовать опцию -P.)

Я не вкопался в исходный код 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 и мой файл "tmp" имеет только 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 char находится в позиции 85 в строке 102321. Я беру эту строку и говорю hexdump, чтобы взять два байта, начиная с позиции 85. Вы можете увидеть специальный (не-7 -bit-ASCII), представленный символом ".", а следующий байт - "M"... так что это однобайтовая кодировка символов.

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

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

Важный вопрос... как я узнаю, что этот символ... не будучи уверенным в кодировке файла? Ответ - это контекст. Я открыл файл, прочитал текст и затем определил, каким персонажем он должен быть. Если я открою его в vim, он отобразится как... потому что vim делает лучшую работу по угадыванию кодировки символов (в данном случае), чем 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 - это правильная кодировка Ö в UTF-8) http://www.utf8-chartable.de/

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

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

$ sed '1s/^/Ö\'$'\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. К сожалению! Просто идет, чтобы показать, как легко заставить кодировки прикручиваться.

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

$ 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 для ввода Ö вместо sed. vim, казалось, лучше определял кодировку (как "latin1", а также ISO-8859-1), поэтому, возможно, он введет новый... с последовательной кодировкой.

$ 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

Christos Zoulas обновил 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)... надеюсь, что когда-нибудь эта версия сделает это с восходящего потока.

Ответ 3

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

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

сначала:

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

второй:

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Пример:

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

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