====== Популярные текстовые форматы данных ======
===== Табулированный текст =====
Табулированный текст, это текстовые файлы в которых ширина столбцов является фиксированной. В таких файлах обычно есть комментарии отмеченные специальными символами.
# 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 существует [[https://tools.ietf.org/html/rfc4180.html|стандарт]], ему почти никто не следует.
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, и бессчетное число других форматов.
Иван
Петров
Московское ш., 101, кв.101
812 123-1234
916 123-4567
XML — иерархическая база данных, состоящая из тегов с аргументами и значений, которые в свою очередь также могут содержать теги.
==== Открытие ====
Существует несколько идейных подходов к тому, как работать с XML. Рассмотрим самый простой и примитивный.
import xml.etree.ElementTree as ET
#Читаем данные из файла
tree = ET.parse('text.xml')
root = tree.getroot()
#Или из строки
root = ET.fromstring("Иван...
# 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) # Все дочерние узлы
# [,
# ,
# ...
# ]
list(root.iter()) # Все дочерние узлы рекурсивно
# [,
# ,
# ...
# ]
list(root[3].findall('phoneNumber')) # Все дочерние узлы с тегом
# [,
# ]
list(root.iter('phoneNumber')) # Все дочерние узлы с тегом рекурсивно
# [,
# ]
==== Модификация ====
#Задание текста
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.parser")
==== Чтение ====
# Доступ к элементу по тегу
titleelem = soup.html.head.title
#Текст элемента
titleelem.string
#Атрибуты
titleelem['width']
#Поиск по значению атрибута (первый элемент)
soup.find('title', align="center")
#Поиск по значению атрибута (все элементы)
soup.findAll('title', width=re.compile('[0-9]+'))
Подробная справка по [[https://www.crummy.com/software/BeautifulSoup/bs4/doc/#making-the-soup|BeautifulSoup]].
==== Запись ====
Одним из самых популярных способов создания HTML (а также любого документа с текстовой разметкой) в Python, является пакет ''jinja2''[pip], см. [[http://jinja.pocoo.org/docs/2.10/|документацию]].
Процесс состоит из двух этапов: создание шаблона и генерация документа.
=== Создание шаблона ===
Шаблоны создаются на специальном [[http://jinja.pocoo.org/docs/2.10/templates/|макроязыке jinja]]. Например:
Hello {{ username }}!
Available tables for you:
{% for item in tables %}
{{ item['name'] }}: {{ item['size'] }}
{% 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 User!
\n
# Available tables for you:\n\n
# test1: 33
\n\n
# test2: 42
\n'
# Сохранение в файл
with open("outfile.txt","w") as fobj:
fobj.write(s)
===== Что еще нужно знать о текстовых форматах =====
* Локализованный Excel создает CSV со следующими параметрами по умолчанию: разделитель полей '';'', символ кавычек ''%%"%%'', символ конца строки ''\r\n''.
* В ''pip'' есть более мощная библиотека для работы с XML: ''lxml''pip, в частности у узлов есть метод ''parent()'' позволяющий получить родительский узел.
* JSON и XML поддерживают механизмы валидации при помощи схем (описание допустимой структуры файла), что полезно, если вы анализируете документ полученный из стороннего источника. См. модули ''jsonschema''pip и [[https://lxml.de/validation.html|модуль валидации библиотеки]] ''lxml''pip.
* У JSON есть идейно (но не синтаксически!) близкий формат [[https://www.tutorialspoint.com/grav/grav_yaml_syntax.htm|YAML]], который считается более человекочитаемым. Для него есть модуль ''pyyaml''pip.
* Не следует использовать регулярные выражения для разбора рекурсивных форматов (XML, YAML, HTML, JSON).