вторник, 19 марта 2019 г.

MiniM. Как использовать CSV файл

Одним из наиболее распространенных форматов передачи данных между программами является формат данных Comma-Separated Values (формат с разделителями запятой). Статья показывает как программа на MiniM может сохранить и прочитать данные в таком формате.

Формат определен как текстовый, построчный, с сохранением одной записи на строку. Разделитель строк - стандартный разделитель перевода строк.

Одной записи данных соответствует одна строка, значения отдельных колонок отделяются друг от друга запятой или точкой с запятой. Далее в статье используется разделитель запятая. Значения колонок, содержащие зарезервированные символы (запятая) обрамляются двойными кавычками ("); если в значении встречаются кавычки — они представляются в файле в виде двух кавычек подряд.

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

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

Построчное чтение текстового файла с передачей каждой строки подпрограмме:
import() ; k  d import^CSV() w
 n dev="|FILE|testdata.csv" o dev:("rte") u dev
 n oldeof=$v("proc",5,0),line
 f  r line q:$zeof  d
 . d showrecord(line)
 i $v("proc",5,oldeof)
 u $p c dev
 q
Показ строки до разбора и показ отдельных значений после парсинга:
showrecord(line)
 n oldio=$io u $p
 w "record:",!,"  "
 w line,!
 n record=$$parserecord(line)
 w "parsed fields:",!
 n i f i=1:1:$ll(record) w "  ",$lg(record,i),!
 u oldio
 q
Парсинг отдельной строки с возвращением набора колонок в виде списка:
parserecord(line)
 ; split record by comma-separated parts
 n resplit="(?:^|,)(\""(?:[^\""]+|\""\"")*\""|[^,]*)"
 n reremove="^\s*""|^\s*,\s*""|^\s*,\s*|^\s*,\s*""|""\s*$|""\s*,\s*$|\s*,\s*$"
 n parts=$zpcres(line,resplit,"g")
 n i f i=1:1:$ll(parts) d
 . ; remove leading and trailing quote symbols and comma if present
 . s $li(parts,i)=$zpcrer($lg(parts,i),reremove,"","g")
 . ; remove doubled quote symbols if present
 . s $li(parts,i)=$zpcrer($lg(parts,i),"""""","""","g")
 q parts
Здесь используется три регулярных выражения - resplit для разбиения на отдельные значения, reremove для удаления форматирующих последовательностей и замена удвоенных кавычек на одинарные.

При импорте CSV файла из примера утилита выводит на экран диагностику с разбором строк и значений:
USER>k  d import^CSV() w
record:
  123  ,    "abc","a b c","a, b, c."  ,  "the ""Abc"" is a name"
parsed fields:
  123
  abc
  a b c
  a, b, c.
  the "Abc" is a name
record:
  "Stri,ng 1", "Stri""ng 2" , String 3,String4
parsed fields:
  Stri,ng 1
  Stri"ng 2
  String 3
  String4
record:
  789,"text","text with spaces","text,with,commas",456
parsed fields:
  789
  text
  text with spaces
  text,with,commas
  456
Утилита справляется с довольно сложным форматированием данных, распознавая случаи лишних пробелов и кавычек.

Для экспорта в примере используется особенность системной функции $ZQUOTE() удваивать кавычки если они есть в строке и добавлять обрамляющие кавычки для значения не являющегося числом. Числа используются как есть, без декорирования, для чисел с дробной частью используется разделитель точка.

Формально говоря, стандарт CSV различает разделитель форматирования в зависимости от локали системы - в локалях, где десятичным разделителем является точка, в качестве табличного разделителя используется запятая; в локалях, где десятичным разделителем является запятая, в качестве табличного разделителя используется точка с запятой. В примере выводятся числа с десятичным разделителем точка, не используя локаль системы. Поэтому в качестве разделителя используется запятая, это наиболее распространенный случай.

Для экспорта данных рутина примера содержит функцию их генерации:
makedata(data)
 k data
 s data($i(data))=$lb(123,"abc","a b c","a, b, c.","the ""Abc"" is a name")
 s data($i(data))=$lb(789,"text","text with spaces","text,with,commas",456)
 q
И сам экспорт состоит из функции построчного вывода и функции вывода одной строки:
export() ; k  d export^CSV() w
 n dev="|FILE|testdata.csv" o dev:("wtn") u dev
 n data d makedata(.data)
 n n="" f  s n=$o(data(n)) q:n=""  d writerecord(data(n))
 u $p c dev
 q
writerecord(record)
 ; in MiniM function $zquote adds leading and trailing quotes
 ; for strings and doubles quotes of string
 n i f i=1:1:$ll(record)-1 w $zquote($lg(record,i)),","
 w $zquote($lg(record,$ll(record))),!
 q
Приведенный пример является лишь демонстрационным и показывает возможности системы MiniM по работе с распространенными форматами файлов данных. В примере используется упрощенное представление строки данных в виде списка. В реальной системе работающей в эксплуатации представление данных может отличаться.

Download csv.zip (zip, 1.1Kb)

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

Отправить комментарий