Если бы был только один входной файл, я бы использовал однострочник Perl.К сожалению, это получается довольно сложно:
perl -pe 'if(/"""/&&s/"""/"/.../"""/&&s/"""/"\n/){s/[\n\r]//;};if(/ = \[([^]]*)]/){$r=$1eq""?"\"\"":$1=~s/"\s*,\s*"/ /gr;s/ = \[([^]]*)]/ = $r/};s/"\s*#[^"\n]*$/"/' one.toml | perl -ne 'if(/^([^"]+) = "(.*)"/){push@k,$1;push@v,"\"$2\""}END{print((join",",@k),"\n",join",",@v)}'
Все становится только хуже, если нам нужно работать с несколькими (*
) файлами одновременно:
perl -ne 'if(/"""/&&s/"""/"/.../"""/&&s/"""/"\n/){s/[\n\r]//;};if(/ = \[([^]]*)]/){$r=$1eq""?"\"\"":$1=~s/"\s*,\s*"/ /gr;s/ = \[([^]]*)]/ = $r/};s/"\s*#[^"\n]*$/"/;print;print"-\n"if eof' *.toml | perl -ne 'if(/^-$/){push@o,join",",@k if scalar@o==0;push@o,join",",@v;@k=@v=()};if(/^([^"]+) = "(.*)"/){push@k,$1;push@v,"\"$2\""}END{print join"\n",@o}'
Эти два фактора вызываютдля структурированного скрипта.Здесь это на Perl, но то же самое можно сделать на Python или любом другом языке, который вам удобен:
#!/usr/bin/env perl
use strict; use warnings; my @output;
foreach my $filename (@ARGV) {
my $content, my @lines, my $replace, my @keys, my @values;
open my $fh, "<:encoding(utf8)", $filename or die "Could not open $filename: $!";
{local $/; $content = <$fh>;}
$content =~ s/"""([^"]*)"""/'"' . $1=~s#[\r\n]##rg . '"'/ge;
@lines = split (/[\r\n]/, $content);
foreach my $line (@lines) {
if ($line =~ m/ = \[([^]]*)]/) {
$replace = $1 eq "" ? '""' : $1 =~ s/"\s*,\s*"/ /gr;
$line =~ s/ = \[([^]]*)]/ = $replace/
}
$line =~ s/"\s*#[^"]*$/"/;
$line =~ m/^([^"]+) = "(.*)"/;
push @keys, $1;
push @values, '"' . $2 . '"'
}
push @output, join ",", @keys if scalar @output == 0;
push @output, join ",", @values
}
print join "\n", @output
Примечания:
Многое из сложностииз-за необходимости иметь дело с массивами (!), комментариями и многострочными строками.Некоторая предварительная обработка необходима для каждого, и именно это занимает большую часть длины решения.Кроме того, потребуется дополнительная информация о возможных угловых случаях и о том, как с ними бороться (например, как разместить массив строк в CSV).Все это только подчеркивает важность качества и согласованности входных данных.Предложенное решение ни в коем случае не является полным или надежным, поскольку оно делает несколько предположений о входных данных и желаемом формате вывода.Вот как я решил упомянутые проблемы:
- значения должны быть только строками, так как они находятся в опубликованном файле примера.Скрипт не обрабатывает числа, даты и логические значения.
- массивы могут быть либо пустыми
[]
, либо массивами строк ["my", "array"]
.В отсутствие четкой спецификации OP они переводят в одну строку, которая является объединением всех строк элементов.В массиве не допускаются разрывы строк, и массив не может содержать другие массивы. - комментарии обрабатываются только в том случае, если они вставляются после строкового значения.Нет строк только для комментариев.
- отступ , пустые строки и заголовки разделов не обрабатываются
Тестовый прогон:
$ perl toml-to-csv.pl *.toml
"someID1","someVersionNumber1","someTag1","someOtherTag1","","long text1","more text1","- text- more text- so much text"
"someID2","someVersionNumber2","someTag2","someOtherTag2","Array","long text2","more text2","- text- more text- so much text"
"someID3","someVersionNumber3","someTag3","someOtherTag3","My array","long text3","more text3","- text- more text- so much text"