Популярные текстовые форматы данных
Табулированный текст
Табулированный текст, это текстовые файлы в которых ширина столбцов является фиксированной. В таких файлах обычно есть комментарии отмеченные специальными символами.
# 1-minute GOES-15 Solar X-ray Flux # UTC Date Time Julian of the # YR MO DA HHMM Day Day Short Long #------------------------------------------------------- 2017 11 06 1643 58063 60180 2.60e-09 4.30e-08 2017 11 06 1644 58063 60240 2.18e-09 4.03e-08 2017 11 06 1645 58063 60300 2.43e-09 3.88e-08 2017 11 06 1646 58063 60360 2.34e-09 3.95e-08 2017 11 06 1647 58063 60420 1.68e-09 4.17e-08
Чтение
Табулированный текст удобно построчно разбирать файл с использованием scanf
pip.
import scanf
with open('test.txt') as file:
for line in file:
if len(line.strip()) == 0 or line[0] == '#':
continue
print(scanf.scanf("%d %d %d %d %d %d %f %f", line))
Запись
Для создания табулированного текста отлично подходит форматированная печать:
data = [[2017, 11, 6, 1643, 58063, 60180, 2.60e-09, 4.30e-08],
[2017, 11, 6, 1644, 58063, 60240, 2.18e-09, 4.03e-08]]
with open('test.txt', 'w') as f:
for d in data:
print("%04d %02d %02d %04d %05d %05d %0.2e %0.2e" % tuple(d), file = f)
CSV
Используются для хранения табличных данных и как формат обмена между приложениями. Хотя на csv существует стандарт, ему почти никто не следует.
1,"Eldon Base for stackable storage shelf, platinum",Muhammed MacIntyre,3,-213.25,38.94,35,Nunavut,Storage & Organization,0.8 2,"1.7 Cubic Foot Compact ""Cube"" Office Refrigerators",Barry French,293,457.81,208.16,68.02,Nunavut,Appliances,0.58 3,"Cardinal Slant-Di Ring Binder, Heavy Gauge Vinyl",Barry French,293,46.71,8.69,2.99,Nunavut,Binders and Binder Accessories,0.39 4,R380,Clay Rozendal,483,1198.97,195.99,3.99,Nunavut,Telephones and Communication,0.58
Для чтения и записи таких файлов в стандартной библиотеке Python есть модуль csv
.
Чтение
Чтение файлов осуществляется через промежуточный объект csv.reader
или csv.DictReader
. Все прочитанные значения будут строками.
import csv
fp = open('sampledata.csv', newline="")
rdr = csv.reader(fp, delimiter=',', quotechar='"')
for rec in rdr:
print(rec)
# ['1', 'Eldon Base for stackable ...
# ['2', '1.7 Cubic Foot Compact ...
fp.close()
Запись
Запись осуществляется через объект csv.writer
или csv.DictWriter
. Методами writerow
(принимает список или словарь соответственно) и writerows
(принимает список списков или словарей соответственно).
import csv
with open('eggs.csv', 'w', newline="") as csvfile:
spamwriter = csv.writer(csvfile, delimiter='\t', quotechar='"')
spamwriter.writerow(["Test", "Value"])
spamwriter.writerow(["1", "2"])
spamwriter.writerows([["2", "3"],["4", "5"]])
Чтение CSV неизвестного формата
В модуле csv
есть объект csv.Sniffer
который позволяет автоматически определить формат CSV файла.
import csv
with open('example.csv', newline='') as csvfile:
dialect = csv.Sniffer().sniff(csvfile.read(1024))
csvfile.seek(0)
reader = csv.reader(csvfile, dialect)
INI
Используются для хранения настроек многими программами.
[sectionA] ValueOne = 42 ValueTwo = yes [sectionB] Test = hg
Для работы с ними используется модуль стандартной библиотеки configparser
.
Чтение
import configparser
config = configparser.ConfigParser()
config.read('example.ini')
print(config['sectionA'].getboolean('ValueTwo'))
# True
print(config['sectionA'].getint('ValueOne'))
# 42
print(config['sectionB'].get('Test'))
# hg
Запись
Структура записи — словарь словарей строк (все значения должны быть приведены к строкам).
import configparser
config = configparser.ConfigParser()
config['sectionA'] = {}
config['sectionA']['ValueTwo'] = 'yes'
config['sectionA']['ValueOne'] = '42'
config['sectionB'] = {}
config['sectionB']['Test'] = 'hg'
with open('example.ini', 'w') as configfile:
config.write(configfile)
JSON
Популярный формат обмена структурированными данными, особенно в веб-приложениях.
{ "firstName": "Иван", "lastName": "Петров", "address": { "streetAddress": "Московское ш., 1, кв.13", "city": "Ленинград", "postalCode": 101101 }, "phoneNumbers": [ "812 123-1234", "916 123-4567" ] }
Структура — рекурсивный словарь. В качестве ключей всегда строки, а в качестве значений могут быть данные типов str
, int
, float
, None
, bool
, списки или словари.
Чтение
Для чтения из файла используется метод json.load
, для чтения из строки json.loads
.
import json
with open('test.json') as jfile:
data = json.load(jfile)
print(d)
# {'firstName': 'Иван', 'lastName': 'Петров', ...
print(d['address']['streetAddress'])
# 'Московское ш., 1, кв.13'
Запись
Для записи в файл используется метод json.dump
,а для создания строки json.dumps
.
import json
data = {"привет":123,
"listval": [1,2,3]}
with open("wtest.json", "w") as jfile:
json.dump(data, jfile, ensure_ascii=False)
Дополнительный ключ ensure_ascii
необходим, чтобы полученный файл был в utf8
, иначе все Unicode символы будут закодированы.
XML
Самый популярный формат структурированных данных. Используется повсеместно, в частности? на нем основаны HTML (то есть весь веб), docx/xlsx/odt/ods, и бессчетное число других форматов.
<person> <firstName>Иван</firstName> <lastName>Петров</lastName> <address city="Ленинград" pcode="101101"> <streetAddress>Московское ш., 101, кв.101</streetAddress> </address> <phoneNumbers> <phoneNumber>812 123-1234</phoneNumber> <phoneNumber>916 123-4567</phoneNumber> </phoneNumbers> </person>
XML — иерархическая база данных, состоящая из тегов с аргументами и значений, которые в свою очередь также могут содержать теги.
Открытие
Существует несколько идейных подходов к тому, как работать с XML. Рассмотрим самый простой и примитивный.
import xml.etree.ElementTree as ET
#Читаем данные из файла
tree = ET.parse('text.xml')
root = tree.getroot()
#Или из строки
root = ET.fromstring("<person><firstName>Иван...
# root - корневой элемент документа имеет тип Element
Чтение состоит в анализе дерева элементов, а запись в его модификации.
Структура объекта Element
root.tag == 'person' # True
root.attrib == {} # True
root.text == '\n ' # True
lmnt = root[2]
lmnt.attrib == {'city': 'Ленинград', 'pcode': '101101'} # True
lmnt.get('city') == 'Ленинград' # True
lmnt[0].tag == 'streetAddress' # True
lmnt[0].text == 'Московское ш., 101, кв.101' # True
Обход объекта Element
list(root) # Все дочерние узлы
# [<Element 'firstName' at 0x7fc59f52c8b8>,
# <Element 'lastName' at 0x7fc59f52cea8>,
# ...
# <Element 'phoneNumbers' at 0x7fc59f316458>]
list(root.iter()) # Все дочерние узлы рекурсивно
# [<Element 'person' at 0x7fc5a0c94098>,
# <Element 'firstName' at 0x7fc59f52c8b8>,
# ...
# <Element 'phoneNumber' at 0x7fc59ee24638>]
list(root[3].findall('phoneNumber')) # Все дочерние узлы с тегом
# [<Element 'phoneNumber' at 0x7fc59ee245e8>,
# <Element 'phoneNumber' at 0x7fc59ee24638>]
list(root.iter('phoneNumber')) # Все дочерние узлы с тегом рекурсивно
# [<Element 'phoneNumber' at 0x7fc59ee245e8>,
# <Element 'phoneNumber' at 0x7fc59ee24638>]
Модификация
#Задание текста
lmnt.text = str('ул. Арбат, д.1, кв.13')
#Установка атрибутов
lmnt.set('city', 'Москва')
#Добавление элемента
lmnt.append(ET.Element('test'))
lmnt.insert(0, ET.Element('test'))
#Удаление элемента
root.remove(lmnt)
Запись
#Создание строки
ET.tostring(root, encoding="unicode")
#Запись в файл
with open("test2.xml","w") as fp:
tree = ET.ElementTree(root)
tree.write(fp, encoding="unicode")
HTML
Язык разметки Web страниц, по сути менее строгая реализация XML. Если разбирать HTML с помощью ET, то вы скорее всего получите ошибку (меньшинство реальных Web страниц генерирует 100% валидный XHTML).
Для работы с HTML есть пакет beautifulsoup4
[pip].
from bs4 import BeautifulSoup
# Из файла
with open("index.html") as fp:
soup = BeautifulSoup(fp, "html.parser")
# Из строки
soup = BeautifulSoup("<html>...", "html.parser")
Чтение
# Доступ к элементу по тегу
titleelem = soup.html.head.title
#Текст элемента
titleelem.string
#Атрибуты
titleelem['width']
#Поиск по значению атрибута (первый элемент)
soup.find('title', align="center")
#Поиск по значению атрибута (все элементы)
soup.findAll('title', width=re.compile('[0-9]+'))
Подробная справка по BeautifulSoup.
Запись
Одним из самых популярных способов создания HTML (а также любого документа с текстовой разметкой) в Python, является пакет jinja2
[pip], см. документацию.
Процесс состоит из двух этапов: создание шаблона и генерация документа.
Создание шаблона
Шаблоны создаются на специальном макроязыке jinja. Например:
Hello <b>{{ username }}</b>!<br/> Available tables for you: {% for item in tables %} <em>{{ item['name'] }}</em>: {{ item['size'] }}<br> {% endfor %}
Генерация документа
import jinja2
# Инициализация jinja
templates_folder = '.'
jenv = jinja2.Environment(
loader=jinja2.FileSystemLoader(templates_folder))
# Загрузка шаблона
template = jenv.get_template('test.j2')
# Раскрытие шаблонов
s = template.render(username="User",
tables=[{'name':'test1',
'size':33},
{'name':'test2',
'size':42}])
# s == 'Hello <b>User</b>!<br/>\n
# Available tables for you:\n\n
# <em>test1</em>: 33<br>\n\n
# <em>test2</em>: 42<br>\n'
# Сохранение в файл
with open("outfile.txt","w") as fobj:
fobj.write(s)
Что еще нужно знать о текстовых форматах
- Локализованный Excel создает CSV со следующими параметрами по умолчанию: разделитель полей
;
, символ кавычек"
, символ конца строки\r\n
. - В
pip
есть более мощная библиотека для работы с XML:lxml
pip, в частности у узлов есть методparent()
позволяющий получить родительский узел. - JSON и XML поддерживают механизмы валидации при помощи схем (описание допустимой структуры файла), что полезно, если вы анализируете документ полученный из стороннего источника. См. модули
jsonschema
pip и модуль валидации библиотекиlxml
pip. - У JSON есть идейно (но не синтаксически!) близкий формат YAML, который считается более человекочитаемым. Для него есть модуль
pyyaml
pip. - Не следует использовать регулярные выражения для разбора рекурсивных форматов (XML, YAML, HTML, JSON).