====== Популярные текстовые форматы данных ====== ===== Табулированный текст ===== Табулированный текст, это текстовые файлы в которых ширина столбцов является фиксированной. В таких файлах обычно есть комментарии отмеченные специальными символами. # 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).