Как в Python3 прочесть данные из текстового файла

newbie

Опубликован:  2018-09-14T06:23:18.176216Z

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

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

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

newbie@stretch:~/workspace/enc$ uchardet sample.win.cue 
windows-1251

и

newbie@stretch:~/workspace/enc$ uchardet sample.utf8.cue 
UTF-8

tKdfK4tyQI.png

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

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

JYG4EaXHI0.png

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

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

5u3UgCtw6R.png

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

>>> uf = open('sample.win.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
>>> 

VxohXCgWmA.png

Упс..!

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

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

dQYqXLN9ty.png

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

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

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

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

vnvmEdb2KC.png

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

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

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

newbie@stretch:~/workspace/enc$ sudo apt-get install python3-chardet
Чтение списков пакетов… Готово
Построение дерева зависимостей       
Чтение информации о состоянии… Готово
Уже установлен пакет python3-chardet самой новой версии (2.3.0-2).
python3-chardet установлен вручную.
обновлено 0, установлено 0 новых пакетов, для удаления отмечено 0 пакетов, и 0 пакетов не обновлено.
newbie@stretch:~/workspace/enc$ 

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

>>> import chardet
>>> 

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

>>> uf = open('sample.win.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[5]
'TITLE "Солнцеклёш"'
>>> 

hf1J9aBzJE.png

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

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

>>> import chardet
>>> uf = open('sample.win.cue', 'rb')
>>> enc = chardet.detect(uf.read())['encoding']
>>> uf.close()
>>> uf = open('sample.win.cue', encoding=enc)
>>> text = uf.read()
>>> uf.close()
>>> len(text)
1510
>>> 

cJVGzzyADj.png

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

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