Прочитайте файл unicode в python, который объявляет его кодировку так же, как источник python

Я хочу написать программу python, которая читает файлы, содержащие текст в Юникоде. Эти файлы обычно кодируются с помощью UTF-8, но могут и не быть; Если это не так, альтернативная кодировка будет явно объявлена ​​в начале файла. Точнее, он будет объявлен с использованием тех же правил, что и сам Python, чтобы позволить исходному коду Python иметь явно объявленную кодировку (как в PEP 0263, см. Https://www.python.org/dev/peps/pep- 0263 / для более подробной информации). Чтобы быть ясным, обрабатываемые файлы на самом деле не являются источником python, но они объявляют свои кодировки (если не в UTF-8) с использованием тех же правил.

Если кто-то знает кодировку файла перед его codecs.open , Python предоставляет очень простой способ прочитать файл с автоматическим декодированием: команда codecs.open ; например, можно было бы:

 import codecs f = codecs.open('unicode.rst', encoding='utf-8') for line in f: print repr(line) 

и каждая line мы получаем в цикле, будет строкой unicode. Есть ли библиотека Python, которая делает аналогичную вещь, но выбор кодировки в соответствии с вышеприведенными правилами (которые, я думаю, являются правилами Python 3.0)? (например, Python раскрывает «прочитанный файл с самозамечаемой кодировкой», который он использует для чтения источника на язык?) Если нет, то какой самый простой способ добиться желаемого эффекта?

Одна мысль состоит в том, чтобы открыть файл, используя обычный open , прочитать первые две строки, интерпретировать их как UTF-8, искать декларацию кодирования с использованием регулярного выражения в PEP, а если найти один начальный декодирование всех последующих строк с использованием кодировки объявлен. Для этого обязательно нужно знать, что для всех кодировок, которые Python позволяет в источнике Python, обычная readline Python будет правильно разделять файл на строки, то есть нам нужно знать, что для всех кодировок Python позволяет в источнике Python байтовая строка '\ n' всегда действительно означает новую строку и не является частью некоторой многобайтовой последовательности, кодирующей другой символ. (На самом деле мне также нужно беспокоиться о «\ r \ n».) Кто-нибудь знает, верно ли это? Документы были не очень конкретными.

Другая мысль – посмотреть в источники Python. Кто-нибудь знает, где в источнике Python выполняется обработка кодировки исходного кода?

  • ElementTree и unicode
  • UnicodeEncodeError при перенаправлении stdout
  • соответствие символов Unicode в регулярных выражениях python
  • Python __str__ против __unicode__
  • Преобразование Unicode в UTF-8 Python
  • Как писать русские символы в файле?
  • Юникод, регулярные выражения и PyPy
  • Как получить URL-адрес не-ascii с помощью urlopen Python?
  • 4 Solutions collect form web for “Прочитайте файл unicode в python, который объявляет его кодировку так же, как источник python”

    Вы должны иметь возможность катить свой собственный декодер в Python. Если вы поддерживаете только 8-битные кодировки, которые являются надмножествами ASCII, код ниже должен работать как есть.

    Если вам нужны поддерживающие 2-байтовые кодировки, такие как UTF-16, вам нужно увеличить шаблон в соответствии с \x00c\x00o.. или наоборот, в зависимости от знака порядка байтов . Сначала создайте несколько тестовых файлов, которые рекламируют свою кодировку:

     import codecs, sys for encoding in ('utf-8', 'cp1252'): out = codecs.open('%s.txt' % encoding, 'w', encoding) out.write('# coding = %s\n' % encoding) out.write(u'\u201chello se\u00f1nor\u201d') out.close() 

    Затем напишите декодер:

     import codecs, re def open_detect(path): fin = open(path, 'rb') prefix = fin.read(80) encs = re.findall('#\s*coding\s*=\s*([\w\d\-]+)\s+', prefix) encoding = encs[0] if encs else 'utf-8' fin.seek(0) return codecs.EncodedFile(fin, 'utf-8', encoding) for path in ('utf-8.txt','cp1252.txt'): fin = open_detect(path) print repr(fin.readlines()) 

    Вывод:

     ['# coding = utf-8\n', '\xe2\x80\x9chello se\xc3\xb1nor\xe2\x80\x9d'] ['# coding = cp1252\n', '\xe2\x80\x9chello se\xc3\xb1nor\xe2\x80\x9d'] 

    Я изучил источники tokenizer.c (спасибо @Ninefingers за предложение этого в другом ответе и ссылку на исходный браузер). Кажется, что точный алгоритм, используемый Python, эквивалентен следующему. В разных местах я опишу алгоритм как чтение байта байтом — очевидно, что на практике нужно что-то буферизовать, но это проще описать. Начальная часть файла обрабатывается следующим образом:

    1. При открытии файла попытайтесь распознать спецификацию UTF-8 в начале файла. Если вы видите это, съешьте его и обратите внимание на то, что вы его видели. Не распознавайте знак порядка байтов UTF-16.
    2. Прочитайте «текст» текста из файла. «Строка» определяется следующим образом: вы продолжаете считывать байты до тех пор, пока не увидите одну из строк «\ n», «\ r» или «\ r \ n» (пытаясь как можно скорее сопоставить строку — это означает, что если вы видите '\ r', вы должны спекулятивно прочитать следующий символ, и если это не «\ n», верните его). Терминатор включен в строку, как это обычно делается на практике Python.
    3. Декодируйте эту строку с помощью кодека UTF-8. Если вы не видели спецификацию UTF-8, генерируйте сообщение об ошибке, если вы видите любые символы, отличные от ASCII (т.е. любые символы выше 127). (Python 3.0, конечно, не генерирует здесь ошибку.) Передайте эту декодированную строку пользователю для обработки.
    4. Попытайтесь интерпретировать эту строку как комментарий, содержащий объявление кодирования, используя регулярное выражение в PEP 0263 . Если вы найдете объявление кодирования, пропустите приведенные ниже инструкции для «Я нашел объявление кодирования».
    5. Хорошо, так что вы не нашли объявление кодирования. Прочитайте еще одну строку с входа, используя те же правила, что и на шаге 2 выше.
    6. Расшифруйте его, используя те же правила, что и в шаге 3, и передайте его пользователю для обработки.
    7. Попытайтесь снова интерпретировать эту строку как комментарий объявления о кодировании, как на шаге 4. Если вы ее найдете, перейдите к приведенным ниже инструкциям для «Я нашел объявление кодирования».
    8. ОК. Мы проверили первые две строки. Согласно PEP 0263, если бы было объявление кодирования, это было бы на первых двух строках, поэтому мы теперь знаем, что мы его не увидим. Теперь мы читаем остальную часть файла, используя те же инструкции чтения, что и для чтения первых двух строк: мы читаем строки, используя правила на шаге 2, декодируем с помощью правил на шаге 3 (делая ошибку, ASCII, если мы не увидели спецификацию).

    Теперь правила для того, что делать, когда « я нашел объявление кодирования »:

    1. Если раньше мы видели спецификацию UTF-8, проверьте, что в заявлении для кодирования указано «utf-8» в той или иной форме. Выбросьте ошибку в противном случае. ('' utf-8 'в какой-то форме' означает все, что после преобразования в нижний регистр и преобразования символов подчеркивания в дефисы является либо литеральной строкой 'utf-8' , либо чем-то, начинающейся с 'utf-8-' .)
    2. Прочитайте остальную часть файла, используя декодер, связанный с данной кодировкой в ​​модуле codecs Python. В частности, разделение остальных байтов в файле на строки – это работа новой кодировки.
    3. Одна окончательная морщина: универсальный материал типа новой линии. Правила здесь заключаются в следующем. Если кодировка – это что-либо, кроме «utf-8» в той или иной форме или «латинский-1» в той или иной форме, вообще не делайте универсальной новой строки; просто передайте строки точно так же, как они поступают из декодера в модуле codecs . С другой стороны, если кодировка является «utf-8» в некоторой форме или «latin-1» в той или иной форме, преобразуйте строки, заканчивающиеся на «\ r» или «\ r \ n», в строки, заканчивающиеся на «\ n». ('' utf-8 'в некотором виде' означает то же, что и раньше. '' latin-1 'в какой-то форме' означает все, что после преобразования в нижний регистр и преобразования подчеркиваний в дефис является одной из буквенных строк 'latin-1' , 'iso-latin-1' или 'iso-8859-1' , или любая строка, начинающаяся с одного из 'latin-1-' , 'iso-latin-1-' или 'iso-8859-1-' .

    Для того, что я делаю, важна верность поведению Python. Мой план состоит в том, чтобы запустить реализацию алгоритма выше в Python и использовать это. Спасибо всем, кто ответил!

    Из упомянутого PEP (0268) :

    Компилятор tokenizer / компилятора Python должен быть обновлен для работы следующим образом:

    1. прочитать файл

    2. декодировать его в Unicode, предполагая фиксированную кодировку для каждого файла

    3. преобразовать его в байтовую строку UTF-8

    4. tokenize содержимое UTF-8

    5. скомпилировать его, создавая объекты Unicode из данных Юникода и создавая строковые объекты из данных литерала Юникода, сначала перекодируя данные UTF-8 в 8-битные строковые данные, используя заданную кодировку файла

    Действительно, если вы проверите Parser/tokenizer.c в источнике Python, вы найдете функции get_coding_spec и check_coding_spec которые несут ответственность за поиск этой информации в строке, рассматриваемой в decoding_fgets .

    Не похоже, что эта возможность подвергается вам в любом случае как API-интерфейс python (по крайней мере, эти конкретные функции не являются префиксом Py , поэтому ваши параметры являются сторонней библиотекой и / или повторно назначают эти функции в качестве расширения. Я лично не знаю каких-либо сторонних библиотек. Я не вижу эту функциональность в стандартной библиотеке.

    Начиная с Python 3.4 есть функция, которая позволяет вам делать то, что вы просите – importlib.util.decode_source

    Согласно документации :

    importlib.util.decode_source(source_bytes)
    Декодируйте заданные байты, представляющие исходный код, и верните его как строку с универсальными символами новой строки (в соответствии с требованиями importlib.abc.InspectLoader.get_source() ).

    Бретт Кэннон рассказывает об этой функции в своем разговоре. От источника к коду: как работает компилятор CPython .

    Interesting Posts

    Почему «python setup.py sdist» создает нежелательные «PROJECT-egg.info» в корневом каталоге проекта?

    hasattr () против блока try-except для обработки несуществующих атрибутов

    Использование super () в методе setter свойства при использовании декоратора @property вызывает AttributeError

    Python – файл в словарь?

    Python super () вызывает TypeError

    Как создать динамические (параметризованные) модульные тесты в python?

    Обработать очень большой (> 20 ГБ) текстовый файл по строкам

    еще одна путаница с ошибкой многопроцессорности, объект 'module' не имеет атрибута 'f'

    Python SKLearn: Логистические вероятности регрессии

    Есть ли способ увеличить django QuerySets с дополнительными атрибутами?

    Конкатенация многих списков в Python

    Pandas Dataframe: развернуть строки со списками в несколько строк с нужной индексацией для всех столбцов

    Python server «Разрешено только одно использование каждого адреса сокета»

    Как осуществляется закрытие?

    Как получить имя запущенного скрипта Python?

    Python - лучший язык программирования в мире.