Как в Python3 прочесть данные из текстового файла
newbie
Опубликован: | 2018-09-14T06:23:18.176216Z |
Отредактирован: | 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
Известно, что для чтения файлов в Питоне предусмотрена встроенная функция open из модуля стандартной библиотеки builtins. Чтобы получить возможность работать с данными из файла, в самом общем случае достаточно вызвать эту функцию, передав ей в качестве параметра имя существующего файла. По умолчанию функция open открывает файлы в режиме rt
и использует предпочтительную кодировку локали. Начну с файла sample.utf8.cue
.
>>> ef = open('sample.utf8.cue') >>> text = ef.read() >>> ef.close() >>> len(text) 1510 >>>
Напомню, файл sample.utf8.cue
имеет кодировку utf-8, которая является предпочтительной кодировкой в моей системе.
>>> import locale >>> locale.getpreferredencoding() 'UTF-8' >>>
Именно поэтому никаких сложностей с открытием этого файла не возникло. А что будет, если попытаться открыть файл другой кодировки? Посмотрим на примере 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 >>>
Упс..!
Функция open может принимать в качестве параметра имя кодировки, с которой следует открывать указанный файл, и если имя кодировки известно, то возникшее препятствие легко обходится.
>>> uf = open('sample.win.cue', encoding='cp1251') >>> text = uf.read() >>> uf.close() >>> len(text) 1510 >>>
А что делать, если имя кодировки файла неизвестно? Здесь есть два пути:
- открыть файл с потерей данных;
- попытаться определить кодировку файла и открыть файл с этой кодировкой.
Да, если имя кодировки файла неизвестно, можно открыть этот файл с потерей данных.
>>> uf = open('sample.win.cue', errors='replace') >>> text = uf.read() >>> uf.close() >>> len(text) 1507 >>>
Но полноценной работы с такими данными не получится. Всё-таки лучше попытаться определить кодировку файла и получить из него все данные.
Для определения кодировки текстовых файлов существует 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 "Солнцеклёш"' >>>
И на выходе у меня получился список 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 >>>
Следует иметь ввиду, что возможности chardet в части списка доступных поддающихся определению кодировок слегка ограничены, подробную информацию можно почерпнуть на странице документации к модулю. Хорошая новость - кириллические кодировки определяются этим детектором безошибочно.