LINUX.ORG.RU

Борьба с кодировками продолжается ... Кракозябры и компания.

 , ,


0

1

Есть необходимость периодически конвертировать текстовые файлы для просмотра в шиндусе и в лине. Конкретно в 1С, которая понимает только CP1251 (ISO-8859-1 тоже, наверное, не суть), который выглядит в Ubuntu по-кракозябрински. При помощи команды iconv -f CP1251 -t UTF-8 (и наоборот) всё это дело прекрасно конвертируется. Но есть нюанс: если вдруг откроешь в Ubuntu файл в любом текстовом редакторе и внесёшь в кракозябринский документ запись на обычной кириллице и сохранишь, команда перестаёт конвертировать, ссылаясь на «illegal input sequence at position» - на позиции как раз там, где я добавил на русском (кириллице) строчку.

На практике вряд ли вообще такое пригодится - вносить изменения. Обычно документы туда-сюда ходят без таких изменений. Я этот досадный эффект обнаружил случайно (наверное, где-то внутри во мне живёт талантливый тестировщик). Вполне возможно, мне не понадобится таким заниматься, то есть вносить изменения в документы. Но вдруг … просто предположим.

Есть какой-то способ конвертировать такой «киш-миш» кодировок в одном документе так, чтобы не было ошибок типа «illegal input … at position»?

Как пример, есть такой вот текст:

Ïðîâåðêà ôàéëà
Проверка файла

Позиция строк и их порядок может быть любой, не обязательно, что на 1 строке такая кодировка, а на 2-ой - вторая. То есть, заранее мы, как будто, знать не можем (например, забыли, что вносили изменения или внёс изменения кто-то другой).

Решение найдено тут

★★★★★

Последнее исправление: Desmond_Hume (всего исправлений: 2)
Ответ на: комментарий от Tigger

Да, например, mousepad это умеет делать. Это простое решение. Но если вдруг понадобилось конвертнуть такой вот с раздельно-выборочной кодировкой файл, то какой командой можно?

Desmond_Hume ★★★★★
() автор топика

Может быть в sed есть какая-то возможность? Например, он обрабатывает по символу все строки и каждый символ обрабатывает стандартно, но в случае illegal символа, например, будет отрабатывать другую команду … к сожалению, не шибко владею sed’ом, поэтому не знаю заранее, насколько это возможно.

Desmond_Hume ★★★★★
() автор топика

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

grem ★★★★★
()

в свое время доУТФных бешенных перекодировок помогала программа Штирлитц маст хев

потом стало попроще, и хватает far или far2l.
открыл файл на редактирование, поигрался с кодировками - скопировал «правильный» текст в буфер обмена, переключился на необходимую кодировку - вставил. файл сохранил.

pfg ★★★★★
()
Последнее исправление: pfg (всего исправлений: 1)

Смешанная кодировка это всегда сложно. Вот, держи небольшой код, который пропускает UTF-8 сквозь себя как есть, а всё, что не прочиталось как UTF-8, конвертирует из кодировки, заданной в командной строке:

#!/usr/bin/python3

from sys import argv, stdin, stdout
import codecs

encoding = argv[1]

def error_handler(exception):
    return (exception.object[exception.start:exception.end].decode(encoding), exception.end)

codecs.register_error('fallback', error_handler)

data = stdin.buffer.read()
res = data.decode('utf-8', errors='fallback')
stdout.buffer.write(res.encode('utf-8'))

Сохрани, скажем, в conv.py и вызывай как

python3 conv.py cp1251 <file

Результатом будет текст в UTF-8. Если файл большой и бОльшая его часть в CP1251, то работать будет медленно (однобайтные куски конвертирует по одному байту, причем каждый байт обрабатывает после ошибки конвертации из UTF-8).

TeopeTuK ★★★★★
()

Кстати тоже всегда удивляло это: и то, что iconv по-дефолту просто падает если увидит что-то непонятное (нет бы ошибку выдать и дальше идти), и то, что способа конвертировать то что конвертируется и копировать как есть то что не конвертируется у него вообще нет, можно либо падать либо игнорировать (т.е. терять).

firkax ★★★★★
()

Попробовал такое:

sed -e 's/Ï/П/; s/ð/р/; s/î/о/; s/â/в/; s/å/е/; s/ê/к/; s/à/а/; s/ô/ф/; s/é/й/; s/ë/л/' myfile.txt

но не сработало. Вообще никак. Ни побуквенно, ни пословно такие крокозябры заменять не хочет.

А, нет, вру. Всё-таки меняется. Наверное, это и есть решение.

Desmond_Hume ★★★★★
() автор топика
Последнее исправление: Desmond_Hume (всего исправлений: 2)
Ответ на: комментарий от Desmond_Hume

А вот еще вариант с тройной перекодировкой, который перекодирует также и валидный UTF-8, который некоторые редакторы записывают, прочитав 8-битный файл как файл в кодировке ISO8859-1:

#!/usr/bin/python3

from sys import argv, stdin, stdout
import codecs

encoding = argv[1]

def error_handler_decode(exception):
    return (exception.object[exception.start:exception.end].decode(encoding), exception.end)

codecs.register_error('fallback', error_handler_decode)

def error_handler_encode(exception):
    return (exception.object[exception.start:exception.end].encode('iso8859-1'), exception.end)

codecs.register_error('isofallback', error_handler_encode)

data = stdin.buffer.read()
res = data.decode('utf-8', errors='fallback').encode(encoding, errors='isofallback').decode(encoding)
stdout.buffer.write(res.encode('utf-8'))
TeopeTuK ★★★★★
()
Ответ на: комментарий от Desmond_Hume
sed -e 's/À/А/; s/à/а/; s/Á/Б/; s/á/б/; s/Â/В/; s/â/в/; s/Ã/Г/; s/ã/г/; s/Ä/Д/; s/ä/д/; s/Å/E/; s/å/е/; s/Æ/Ж/; s/æ/ж/; s/Ç/З/; s/ç/з/; s/È/И/; s/è/и/; s/É/Й/; s/é/й/; s/Ê/К/; s/ê/к/; s/Ë/Л/; s/ë/л/; s/Ì/М/; s/ì/м/; s/Í/Н/; s/í/н/; s/Î/О/; s/î/о/; s/Ï/П/; s/ï/п/; s/Ð/Р/; s/ð/р/; s/Ñ/С/; s/ñ/с/; s/Ò/Т/; s/ò/т/; s/Ó/У/; s/ó/y/; s/Ô/Ф/; s/ô/ф/; s/Õ/Х/; s/õ/х/; s/Ö/Ц/; s/ö/ц/; s/×/Ч/; s/÷/ч/; s/Ø/Ш/; s/ø/ш/; s/Ù/Щ/; s/ù/щ/; s/Ú/Ъ/; s/ú/ъ/; s/Û/Ы/; s/û/ы/; s/Ü/Ь/; s/ü/ь/; s/Ý/Э/; s/ý/э/; s/Þ/Ю/; s/þ/ю/; s/ß/Я/; s/ÿ/я/; s/¹/№/'

Этот «шарф» sed’a не помог, какие-то буквы заменяются нормально, какие-то нет. Sed тоже любит какие-то условия, чтобы «звёзды сошлись», оказывается …

Desmond_Hume ★★★★★
() автор топика
echo "Ïîýòîìó îíà ìîæåò îòëè÷àòüñÿ îò äàííûõ" | sed -e 's/À/А/; s/à/а/; s/Á/Б/; s/á/б/; s/Â/В/; s/â/в/; s/Ã/Г/; s/ã/г/; s/Ä/Д/; s/ä/д/; s/Å/E/; s/å/е/; s/Æ/Ж/; s/æ/ж/; s/Ç/З/; s/ç/з/; s/È/И/; s/è/и/; s/É/Й/; s/é/й/; s/Ê/К/; s/ê/к/; s/Ë/Л/; s/ë/л/; s/Ì/М/; s/ì/м/; s/Í/Н/; s/í/н/; s/Î/О/; s/î/о/; s/Ï/П/; s/ï/п/; s/Ð/Р/; s/ð/р/; s/Ñ/С/; s/ñ/с/; s/Ò/Т/; s/ò/т/; s/Ó/У/; s/ó/y/; s/Ô/Ф/; s/ô/ф/; s/Õ/Х/; s/õ/х/; s/Ö/Ц/; s/ö/ц/; s/×/Ч/; s/÷/ч/; s/Ø/Ш/; s/ø/ш/; s/Ù/Щ/; s/ù/щ/; s/Ú/Ъ/; s/ú/ъ/; s/Û/Ы/; s/û/ы/; s/Ü/Ь/; s/ü/ь/; s/Ý/Э/; s/ý/э/; s/Þ/Ю/; s/þ/ю/; s/ß/Я/; s/ÿ/я/; s/¹/№/'
 
Поэтîмy îна ìîжеò îòличàòься îò дàííых

«Поэтому она может отличаться от данных» - не расшифровалось. Странно …

Desmond_Hume ★★★★★
() автор топика
Ответ на: комментарий от Desmond_Hume

Всё, нашёл причину! Вот правильная команда:

sed -e 's/À/А/g; s/à/а/g; s/Á/Б/g; s/á/б/g; s/Â/В/g; s/â/в/g; s/Ã/Г/g; s/ã/г/g; s/Ä/Д/g; s/ä/д/g; s/Å/E/g; s/å/е/g; s/Æ/Ж/g; s/æ/ж/g; s/Ç/З/g; s/ç/з/g; s/È/И/g; s/è/и/g; s/É/Й/g; s/é/й/g; s/Ê/К/g; s/ê/к/g; s/Ë/Л/g; s/ë/л/g; s/Ì/М/g; s/ì/м/g; s/Í/Н/g; s/í/н/g; s/Î/О/g; s/î/о/g; s/Ï/П/g; s/ï/п/g; s/Ð/Р/g; s/ð/р/g; s/Ñ/С/g; s/ñ/с/g; s/Ò/Т/g; s/ò/т/g; s/Ó/У/g; s/ó/y/g; s/Ô/Ф/g; s/ô/ф/g; s/Õ/Х/g; s/õ/х/g; s/Ö/Ц/g; s/ö/ц/g; s/×/Ч/g; s/÷/ч/g; s/Ø/Ш/g; s/ø/ш/g; s/Ù/Щ/g; s/ù/щ/g; s/Ú/Ъ/g; s/ú/ъ/g; s/Û/Ы/g; s/û/ы/g; s/Ü/Ь/g; s/ü/ь/g; s/Ý/Э/g; s/ý/э/g; s/Þ/Ю/g; s/þ/ю/g; s/ß/Я/g; s/ÿ/я/g; s/¹/№/g'

Забыл глобальное применение применить («g» на конце). Вот так работает, как надо! Sed реально крут!

Desmond_Hume ★★★★★
() автор топика
Последнее исправление: Desmond_Hume (всего исправлений: 1)