Python3, читаем данные из текстового файла

avm

Опубликован:  2018-02-14T04:41:59.212567Z
1100

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

Один из вариантов чтения текстовых файлов c одновременным определением их кодировки я сейчас разберу в деталях. Для этого мне понадобится пара файлов: sample.cue и sample1.cue, которые имеют различную кодировку.

sadmin@debian:~/workspace/cuetoolkit$ uchardet sample.cue
windows-1251

и

sadmin@debian:~/workspace/cuetoolkit$ uchardet sample1.cue
UTF-8

Известно, что для чтения файлов в Питоне предусмотрена встроенная функция open из модуля стандартной библиотеки builtins. Чтобы получить возможность работать с данными из файла, в самом общем случае достаточно вызвать эту функцию, передав ей в качестве параметра имя существующего файла. По умолчанию функция open открывает файлы в режиме rt и использует предпочтительную кодировку локали. Начну с файла sample1.cue.

>>> ef = open('sample1.cue')
>>> text = ef.read()
>>> ef.close()
>>> len(text)
1510
>>> 

Напомню, файл sample1.cue имеет кодировку utf-8, которая является предпочтительной кодировкой в моей системе.

>>> import locale
>>> locale.getpreferredencoding()
'UTF-8'
>>> 

Именно поэтому никаких сложностей с открытием этого файла не возникло. А что будет, если попытаться открыть файл другой кодировки? Посмотрим на примере sample.cue.

>>> uf = open('sample.cue')
>>> text = uf.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.5/codecs.py", line 321, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xcc in position 100: invalid continuation byte
>>> 

Упс..!
Функция open может принимать в качестве параметра имя кодировки, с которой следует открывать указанный файл, и если имя кодировки известно, то возникшее препятствие легко обходится.

>>> uf = open('sample.cue', encoding='cp1251')
>>> text = uf.read()
>>> uf.close()
>>> len(text)
1510
>>> 

А что делать, если имя кодировки файла неизвестно? Здесь есть два пути:

  • попытаться определить кодировку файла;
  • открыть файл с потерей данных.

Да, если имя кодировки файла неизвестно, можно открыть этот файл с потерей данных.

>>> uf = open('sample.cue', errors='replace')
>>> text = uf.read()
>>> uf.close()
>>> len(text)
1507
>>> 

Но полноценной работы с такими данными не получится. Всё-таки лучше попытаться определить кодировку файла и получить из него все данные.

Для определения кодировки текстовых файлов существует The Universal Character Encoding Detector, который портирован на многие диалекты, в том числе и на Питон. Именно этот детектор кодировки использует утилита uchardet, с помощью которой я показал кодировку подопытных файлов выше. Реализация этого детектора кодировки на Питоне доступна на github.com.

В Debian stretch chardet для Питона третьей версии можно установить из официального репозитория.

sadmin@debian:~/workspace/cuetoolkit$ sudo aptitude install python3-chardet
python3-chardet уже установлен в запрошенной версии (2.3.0-2)
python3-chardet уже установлен в запрошенной версии (2.3.0-2)
Ни одного пакета не будет установлено, обновлено или удалено.
0 пакетов обновлено, 0 установлено новых, 0 пакетов отмечено для удаления, и 0 пакетов не обновлено.
Необходимо получить 0 Б архивов. После распаковки 0 Б будет занято.

sadmin@debian:~/workspace/cuetoolkit$ 

Для определения кодировки указанного файла нужно импортировать chardet:

>>> import chardet
>>> 

открыть файл в бинарном режиме:

>>> uf = open('sample.cue', 'rb')
>>> 

и определить его кодировку:

>>> enc = chardet.detect(uf.read())['encoding']
>>> enc
'windows-1251'
>>> 

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

>>> uf.seek(0)
0
>>> data = [line.decode(enc).rstrip() for line in uf]
>>> uf.close()
>>> len(data)
66
>>> 

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

Следует иметь ввиду, что возможности chardet в части списка доступных поддающихся определению кодировок слегка ограничены, подробную информацию можно почерпнуть на странице документации к модулю. Хорошая новость - кириллические кодировки определяются этим детектором безошибочно.

 
Комментарии: