LINUX.ORG.RU

Ещё одна пичалька с Python3 и строками в неправильных кодировках...

 , ,


1

3

Есть у меня, братцы, str-строка:

'ïàðàìåòðû îòîáðàæåíèÿ êîíêðåòíîãî çíàêà ïî øàáëîíó'

Хочу я конвертнуть её в нормальную человеческую str-строку c русским текстом. Как бы мне такое сделать?

P.S. http://www.online-decoder.com/ru говорит, что там надо проделать ISO-8859-1 --> CP-1251, если что.


Ответ на: комментарий от makoven

О! ты не сталкивался с двойными, а порой и тройными перекодировками(больше мне не встречалось, но знаю что было) почты в 90х? Тебе повезло (:

uCore
()
Ответ на: комментарий от makoven

.encode('ISO-8859-1') - преобразовать строку в строку Unicode, исходя из того, что строка-источник в кодировке 'ISO-8859-1'.
.decode('cp1251') - преобразовать строку Unicode в строку с кодировкой 'cp1251'.

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

ïàðàìåòðû îòîáðàæåíèÿ êîíêðåòíîãî çíàêà ïî øàáëîíó

А так понимаю, что изначально был текст на cp1251, который открыли как Latin-1? Но то, что мы видим тут, это на самом деле не Latin-1, а юникод. То-есть какая-то програма (редактор, браузер) отыскала все эти Latin-1 символы (î, ò) в юниокдной таблице и вывела UTF-8 строку. И это нам еще повезло, все символы нашлись в таблице. Могли ведь и не найтись для другой кодировки?

И полученная юникодная строка на уровне байтов уже вовсе ни cp1251 ни Latin-1, а cp1251 => Latin-1 => Utf-8?

Далее непонятен смысл слов encode и decode. По-моему в таких названиях мало смысла. Почему не наоборот, например? Не проще было назвать их to_bytes и to_str?

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

.encode('ISO-8859-1') - преобразовать строку в строку Unicode

А не в байт-массив?:

>>> 'ïàðàìåòðû îòîáðàæåíèÿ êîíêðåòíîãî çíàêà ïî øàáëîíó'.encode('ISO-8859-1')
b'\xef\xe0\xf0\xe0\xec\xe5\xf2\xf0\xfb \xee\xf2\xee\xe1\xf0\xe0\xe6\xe5\xed\xe8\xff \xea\xee\xed\xea\xf0\xe5\xf2\xed\xee\xe3\xee \xe7\xed\xe0\xea\xe0 \xef\xee \xf8\xe0\xe1\xeb\xee\xed\xf3'  

decode('cp1251') - преобразовать строку Unicode в строку с кодировкой 'cp1251'

А может преобразовать байт-массив в utf-8, рассматривая байты, как имеющие кодировку cp1521?

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

Поговорил сам с собой и разобрался)

В третьем питоне все строки UTF-8. Поэтому encode('ISO-8859-1') означает «закодировать UTF-8 строку в ISO-8859-1». Именно закодировать, т.к. результат будет в нестандартной для питона кодировке. Результат, логично, получается байт-строкой, поскольку ISO-8859-1 - 8-битная кодировка. Берем UTF-8 символ (1-4 байта) ищем аналогично выглядящий символ в ISO-8859-1 (падаем с ошибкой если не находим), выводим код этого символа из таблицы ISO-8859-1

decode('cp1251') означает «раскодировать байт-строку в стандартную для третьепитона UTF-8 строку». Берем байт, смотрим как он выглядит в cp1251 кодировке (падаем с ошибкой если не находим), ищем аналогично выглядящий символ в юникод-таблице и выводим его в UTF-8

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

Дополнение. Вот это:

Результат, логично, получается байт-строкой, поскольку ISO-8859-1 - 8-битная кодировка

ниочем, т.к. .encode('UTF-32') тоже кодирует в байт-строку (по 8 байт на каждый символ)

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

если речь идет о python3, то на входе у тебя юникодная строка. я думаю, смысл отдельно encode и decode тебе понятен, но здесь они использованы последовательно

тут можно рассуждать задом наперед: у тебя где-то в файле была строка(байты) в cp1251, которую кто-то прочитал так, будто она была в iso-8859-1, и записал в юникоде (т.е. сделал bytes.decode('iso-8859-1')). получилось 'ïàðàìåòðû îòîáðàæåíèÿ êîíêðåòíîãî çíàêà ïî øàáëîíó'. так что сперва тебе надо получить исходный массив байт (str.encode('iso-8859-1') - обратка к тому, что выше), а затем перекодировать в юникод, как полагалось изначально (bytes.decode('cp1251'))

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

В третьем питоне объект str — абстрактное хранилище Unicode символов. В какой оно реально кодировке внутри хранится — деталь реализации (на практике емнип либо utf8 либо utf16 в зависимости от параметров билда, но это не важно).

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

То-есть, по условиям задачи не понятно, что же лежит в файле:

  • Чистый cp1251, который ОП нерадиво открыл как Latin-1
  • Latin-1, неверно перекодированный из cp1251
  • или же одна из Юникод-кодировок, перекодированная из Latin-1, перекодированная из cp1251

Всё зависит от того, как и в каком редакторе ОП открыл файл, чтобы затем скопировать текст сюда, в браузер

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

То-есть, по условиям задачи не понятно, что же лежит в файле:

в первых двух случаях решение, вроде, одно и то же. в третий я не въехал, но, походу, то же самое

я исходил из того, что твой рецепт в первом комменте решил проблему. файл в cp-1251 - это только для примера в объяснении

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

Правда не до конца понимаю, что здесь происходит. Буду признателен, если кто-нибудь по полочкам разберет

Надо понять очень простую штуку: внутре Python оперирует Юникодом, для него это родное представление текста. И да, объекты Юникода — это не строки, у строк всегда есть кодировка. Кодировки-шмодировки — это суть способы представления Юникода. Поэтому, чтобы работать в Python с Юникодом, строки нужно раскодировать, этим и занимается метод decode — декодирует из строки в какой-то кодировке в Юникод. Соответственно, чтобы из Юникода получить строку в некой кодировке, используется метод encode, который кодирует последовательность юникодных символов в байты согласно указанной кодовой странице.

'ïàðàìåòðû îòîáðàæåíèÿ êîíêðåòíîãî çíàêà ïî øàáëîíó'.encode('ISO-8859-1').decode('cp1251')
Это какая-то ересь: строку в неизвестной кодировке пытаются кодировать в строку в кодировке ISO-8859-1, что неминуемо закончится фейлом.

Но если предположить, что кто-то невнимательно копипастил и переписать пример как

>>> print u'ïàðàìåòðû îòîáðàæåíèÿ êîíêðåòíîãî çíàêà ïî øàáëîíó'.encode('ISO-8859-1').decode('cp1251')
параметры отображения конкретного знака по шаблону
>>> 
то всё ладно и складно, потому здесь в строку в кодировке ISO-8859-1 кодируется Юникод.

Да, в случае Python 3 и изначальный пример корректен, поскольку в третьем питоне строки — это Юникод объекты, а не последовательности байтов, как в Python 2.

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

Как так получилось, что 99% программистов не способны корректно работать с юникодом?

anonymous
()
Ответ на: комментарий от Virtuos86

Это какая-то ересь:
print u'

Это просто у тебя второй питон, а не ересь.

anonymous
()
Ответ на: комментарий от anonymous

Потому что юникодные символы перестали быть простыми байтами и работа со строками как с массивами стала как минимум затруднительной.

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

Потому что юникодные символы перестали быть простыми байтами и работа со строками как с массивами стала как минимум затруднительной.

Работа со строками как с массивами знаков вообще не изменилась. Проблемы от того, что некоторые буратины считают из говно-си, что строки - это массивы байтов.

anonymous
()
Ответ на: комментарий от anonymous

Но но но... Ведь строки это байты, как ни крути! Юникодные строки это внутреннее представление теперь, но их можно закодировать обратно в байты. Видимо, ты не часто работаешь с непечатаемыми символами, от которых даже юникод ломается. Ну и есть такие приколы, что в венде юникод чисто свой и ничего не работает, вот тебе пример — https://en.wikipedia.org/wiki/Tilde#Unicode_and_Shift_JIS_encoding_of_wave_dash

anonymous
()
Ответ на: комментарий от anonymous

Одна история лучше другой. Непечатаемые символы, юникод сломается. Shift JIS против CP932. Что ты несёшь-то вообще?

anonymous
()
Ответ на: комментарий от anonymous

Поработай со строками с той тильдой в венде. Питон (да и большинство приложений когда ты будешь обрабатывать этот кейс) у тебя просто посыпется из-за таких приколов. Если ты не способен этого понять, это не значит что проблема не в тебе.

anonymous
()
Ответ на: комментарий от anonymous

Это у тебя моск посыпался, если ты не способен осознать отличие Shist JIS от CP932 и отличие Юникода от байтовых кодировок.

Можешь кидать свой код, который считаешь сложным - посмеёмся.

anonymous
()
Ответ на: комментарий от anonymous

Ведь строки это байты, как ни крути!

Любой объект - это байты, как ни крути. Но ты ж не прёшь работать со списком флоатов как с массивом байтов? Сериализовал список флоатов в байты - всё, ты работаешь с байтами. Чтобы работать со списком флоатов нужно десереализировать обратно.

Сериализовал строку в байты - у тебя тупой набор байтов. Хочешь работать с массивом знаков - десереализуй обратно.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.