У меня есть несколько текстовых файлов (utf-8), которые я хочу обработать в сценарии оболочки. Это не совсем тот же формат, но если бы я мог только разбить их на съедобные куски, я бы справился с этим. Это можно было бы запрограммировать на C или python, но я предпочитаю этого не делать.
EDIT: I wrote a solution in C; see my own answer. I think this may be the simplest approach after all. If you think I'm wrong please test your solution against the more complicated example input from my answer below.
-- jcxz100
Для ясности (и для упрощения отладки) я хочу, чтобы фрагменты сохранялись в виде отдельных текстовых файлов в подпапке.
Все типы входных файлов состоят из:
Я хочу разбить ввод только в соответствии с совпадающими парами скобок / скобок высший уровень. Запрещается изменять полезную нагрузку внутри этих пар (включая символы новой строки и пробелы). Все, что находится за пределами пар верхнего уровня, следует выбросить как мусор.
Любой мусор или полезная нагрузка внутри двойных кавычек должны рассматриваться как атомный (обрабатываться как необработанный текст, поэтому любые квадратные или круглые скобки внутри также должны рассматриваться как текст).
Вот пример (с использованием только пар {}):
junk text
"atomic junk"
some junk text followed by a start bracket { here is the actual payload
more payload
"atomic payload"
nested start bracket { - all of this line is untouchable payload too
here is more payload
"yet more atomic payload; this one's got a smiley ;-)"
end of nested bracket pair } - all of this line is untouchable payload too
this is payload too
} trailing junk
intermittent junk
{
payload that goes in second output file }
end junk
... извините: некоторые из входных файлов действительно такие беспорядочные.
Первый выходной файл должен быть:
{ here is the actual payload
more payload
"atomic payload"
nested start bracket { - all of this line is untouchable payload too
here is more payload
"yet more atomic payload; this one's got a smiley ;-)"
end of nested bracket pair } - all of this line is untouchable payload too
this is payload too
}
... и второй выходной файл:
{
payload that goes in second output file }
Примечание:
Я еще не совсем решил, нужно ли хранить пару начальных / конечных символов в выводе или их самих следует отбросить как мусор. Я думаю, что решение, которое их удерживает, является более универсальным.
В одном входном файле могут быть разные типы пар скобок / парантезов верхнего уровня.
Осторожно: во входных файлах есть символы * и $, поэтому, пожалуйста, не запутайте bash ;-)
Я предпочитаю удобочитаемость краткости; но не за счет экспоненциальной потери скорости.
Приятно иметь:
Внутри текста есть двойные кавычки, экранированные обратной косой чертой; желательно с ними обращаться (У меня есть хак, но он некрасивый).
Сценарий не должен нарушать несовпадающие пары скобок / круглых скобок в мусоре и / или полезной нагрузке (примечание: внутри атомики разрешено использование должен!)
Еще-далеко-приятное-иметь:
Я еще не видел этого, но можно предположить, что некоторые входные данные могут иметь одинарные кавычки, а не двойные кавычки для обозначения атомарного содержимого ... или даже сочетание того и другого.
Было бы неплохо, если бы сценарий можно было легко изменить для анализа ввода аналогичной структуры, но с разными начальными / конечными символами или строками.
Я вижу, что это непросто, но я думаю, что это не даст надежного решения, если я разбью его на более простые вопросы.
Основная проблема заключается в правильном разделении ввода - все остальное можно проигнорировать или «решить» с помощью хаков, поэтому не стесняйтесь игнорировать приятный и еще-далекое-хорошее-иметь.
Я дал вам хороший старт в Perl
. Может быть, поиграйте с этим постом в конкретный вопрос Perl, если вы его не понимаете.
Спасибо @dawg. Как вы думаете, Perl - лучший путь вперед? Сейчас я все равно подумываю написать (и, конечно же, опубликовать) программу на c, так как это может быть для меня проще, чем изучение Perl :)
Есть много способов снять шкуру. Если вам нужно решение быстрый, Python, Ruby или Perl будут самыми быстрыми решениями. Конечно, вы можете написать что-нибудь на C, но это потребует больше усилий.
Правильно. Итак, я написал свою первую программу на языке C за 8 лет. Это решает мою проблему; включая все, кроме одного еще-далеко-далеко-хорошо-иметь. Теперь мне интересно, разместить его здесь или открыть и ответить на новый вопрос?
Я бы ответь на свой вопрос (и подумал бы о том, чтобы проголосовать за тех, кто тратит время, помогая вам ...)
@dawg: у меня низкая репутация (13), чтобы написать или проголосовать за вас, но спасибо :)
Данный:
$ cat file
junk text
"atomic junk"
some junk text followed by a start bracket { here is the actual payload
more payload
"atomic payload"
nested start bracket { - all of this line is untouchable payload too
here is more payload
"yet more atomic payload; this one's got a smiley ;-)"
end of nested bracket pair } - all of this line is untouchable payload too
this is payload too
} trailing junk
intermittent junk
{
payload that goes in second output file }
end junk
Этот файл perl извлечет описанные вами блоки в файлы block_1
, block_2
и т. д.:
#!/usr/bin/perl
use v5.10;
use warnings;
use strict;
use Text::Balanced qw(extract_multiple extract_bracketed);
my $txt;
while (<>){$txt.=$_;} # slurp the file
my @blocks = extract_multiple(
$txt,
[
# Extract {...}
sub { extract_bracketed($_[0], '{}') },
],
# Return all the fields
undef,
# Throw out anything which does not match
1
);
chdir "/tmp";
my $base = "block_";
my $cnt=1;
for my $block (@blocks){ my $fn = "$base$cnt";
say "writing $fn";
open (my $fh, '>', $fn) or die "Could not open file '$fn' $!";
print $fh "$block\n";
close $fh;
$cnt++;}
Теперь файлы:
$ cat block_1
{ here is the actual payload
more payload
"atomic payload"
nested start bracket { - all of this line is untouchable payload too
here is more payload
"yet more atomic payload; this one's got a smiley ;-)"
end of nested bracket pair } - all of this line is untouchable payload too
this is payload too
}
$ cat block_2
{
payload that goes in second output file }
Использование Text::Balanced
- надежное и, вероятно, лучшее решение.
Вы может делаете блоки с помощью одного Perl регулярное выражение:
$ perl -0777 -nlE 'while (/(\{(?:(?1)|[^{}]*+)++\})|[^{}\s]++/g) {if ($1) {$cnt++; say "block $cnt:== start:\n$1\n== end";}}' file
block 1:== start:
{ here is the actual payload
more payload
"atomic payload"
nested start bracket { - all of this line is untouchable payload too
here is more payload
"yet more atomic payload; this one's got a smiley ;-)"
end of nested bracket pair } - all of this line is untouchable payload too
this is payload too
}
== end
block 2:== start:
{
payload that goes in second output file }
== end
Но это немного более хрупко, чем использование правильного парсера, такого как Text::Balanced
...
У меня есть решение на C. Казалось бы, слишком много сложностей, чтобы это можно было легко реализовать в сценарии оболочки. Программа не слишком сложна, но, тем не менее, содержит более 200 строк кода, которые включают проверку ошибок, некоторую оптимизацию скорости и другие тонкости.
Исходный файл разделить скобки на блоки. c:
#include <stdio.h>
/* Example code by jcxz100 - your problem if you use it! */
#define BUFF_IN_MAX 255
#define BUFF_IN_SIZE (BUFF_IN_MAX+1)
#define OUT_NAME_MAX 31
#define OUT_NAME_SIZE (OUT_NAME_MAX+1)
#define NO_CHAR '\0'
int main()
{
char pcBuff[BUFF_IN_SIZE];
size_t iReadActual;
FILE *pFileIn, *pFileOut;
int iNumberOfOutputFiles;
char pszOutName[OUT_NAME_SIZE];
char cLiteralChar, cAtomicChar, cChunkStartChar, cChunkEndChar;
int iChunkNesting;
char *pcOutputStart;
size_t iOutputLen;
pcBuff[BUFF_IN_MAX] = '\0'; /* ... just to be sure. */
iReadActual = 0;
pFileIn = pFileOut = NULL;
iNumberOfOutputFiles = 0;
pszOutName[OUT_NAME_MAX] = '\0'; /* ... just to be sure. */
cLiteralChar = cAtomicChar = cChunkStartChar = cChunkEndChar = NO_CHAR;
iChunkNesting = 0;
pcOutputStart = (char*)pcBuff;
iOutputLen = 0;
if ((pFileIn = fopen("input-utf-8.txt", "r")) == NULL)
{
printf("What? Where?\n");
return 1;
}
while ((iReadActual = fread(pcBuff, sizeof(char), BUFF_IN_MAX, pFileIn)) > 0)
{
char *pcPivot, *pcStop;
pcBuff[iReadActual] = '\0'; /* ... just to be sure. */
pcPivot = (char*)pcBuff;
pcStop = (char*)pcBuff + iReadActual;
while (pcPivot < pcStop)
{
if (cLiteralChar != NO_CHAR) /* Ignore this char? */
{
/* Yes, ignore this char. */
if (cChunkStartChar != NO_CHAR)
{
/* ... just write it out: */
fprintf(pFileOut, "%c", *pcPivot);
}
pcPivot++;
cLiteralChar = NO_CHAR;
/* End of "Yes, ignore this char." */
}
else if (cAtomicChar != NO_CHAR) /* Are we inside an atomic string? */
{
/* Yup; we are inside an atomic string. */
int bBreakInnerWhile;
bBreakInnerWhile = 0;
pcOutputStart = pcPivot;
while (bBreakInnerWhile == 0)
{
if (*pcPivot == '\\') /* Treat next char as literal? */
{
cLiteralChar = '\\'; /* Yes. */
bBreakInnerWhile = 1;
}
else if (*pcPivot == cAtomicChar) /* End of atomic? */
{
cAtomicChar = NO_CHAR; /* Yes. */
bBreakInnerWhile = 1;
}
if (++pcPivot == pcStop) bBreakInnerWhile = 1;
}
if (cChunkStartChar != NO_CHAR)
{
/* The atomic string is part of a chunk. */
iOutputLen = (size_t)(pcPivot-pcOutputStart);
fprintf(pFileOut, "%.*s", iOutputLen, pcOutputStart);
}
/* End of "Yup; we are inside an atomic string." */
}
else if (cChunkStartChar == NO_CHAR) /* Are we inside a chunk? */
{
/* No, we are outside a chunk. */
int bBreakInnerWhile;
bBreakInnerWhile = 0;
while (bBreakInnerWhile == 0)
{
/* Detect start of anything interesting: */
switch (*pcPivot)
{
/* Start of atomic? */
case '"':
case '\'':
cAtomicChar = *pcPivot;
bBreakInnerWhile = 1;
break;
/* Start of chunk? */
case '{':
cChunkStartChar = *pcPivot;
cChunkEndChar = '}';
break;
case '[':
cChunkStartChar = *pcPivot;
cChunkEndChar = ']';
break;
case '(':
cChunkStartChar = *pcPivot;
cChunkEndChar = ')';
break;
case '<':
cChunkStartChar = *pcPivot;
cChunkEndChar = '>';
break;
}
if (cChunkStartChar != NO_CHAR)
{
iNumberOfOutputFiles++;
printf("Start '%c' '%c' chunk (file %04d.txt)\n", *pcPivot, cChunkEndChar, iNumberOfOutputFiles);
sprintf((char*)pszOutName, "output/%04d.txt", iNumberOfOutputFiles);
if ((pFileOut = fopen(pszOutName, "w")) == NULL)
{
printf("What? How?\n");
fclose(pFileIn);
return 2;
}
bBreakInnerWhile = 1;
}
else if (++pcPivot == pcStop)
{
bBreakInnerWhile = 1;
}
}
/* End of "No, we are outside a chunk." */
}
else
{
/* Yes, we are inside a chunk. */
int bBreakInnerWhile;
bBreakInnerWhile = 0;
pcOutputStart = pcPivot;
while (bBreakInnerWhile == 0)
{
if (*pcPivot == cChunkStartChar)
{
/* Increase level of brackets/parantheses: */
iChunkNesting++;
}
else if (*pcPivot == cChunkEndChar)
{
/* Decrease level of brackets/parantheses: */
iChunkNesting--;
if (iChunkNesting == 0)
{
/* We are now outside chunk. */
bBreakInnerWhile = 1;
}
}
else
{
/* Detect atomic start: */
switch (*pcPivot)
{
case '"':
case '\'':
cAtomicChar = *pcPivot;
bBreakInnerWhile = 1;
break;
}
}
if (++pcPivot == pcStop) bBreakInnerWhile = 1;
}
iOutputLen = (size_t)(pcPivot-pcOutputStart);
fprintf(pFileOut, "%.*s", iOutputLen, pcOutputStart);
if (iChunkNesting == 0)
{
printf("File done.\n");
cChunkStartChar = cChunkEndChar = NO_CHAR;
fclose(pFileOut);
pFileOut = NULL;
}
/* End of "Yes, we are inside a chunk." */
}
}
}
if (cChunkStartChar != NO_CHAR)
{
printf("Chunk exceeds end-of-file. Exiting gracefully.\n");
fclose(pFileOut);
pFileOut = NULL;
}
if (iNumberOfOutputFiles == 0) printf("Nothing to do...\n");
else printf("All done.\n");
fclose(pFileIn);
return 0;
}
Я решил приятный и один из еще-далекое-хорошее-иметь. Чтобы показать это, ввод немного сложнее, чем пример в вопросе:
junk text
"atomic junk"
some junk text followed by a start bracket { here is the actual payload
more payload
'atomic payload { with start bracket that should be ignored'
nested start bracket { - all of this line is untouchable payload too
here is more payload
"this atomic has a literal double-quote \" inside"
"yet more atomic payload; this one's got a smiley ;-) and a heart <3"
end of nested bracket pair } - all of this line is untouchable payload too
this is payload too
"here's a totally unprovoked $ sign and an * asterisk"
} trailing junk
intermittent junk
<
payload that goes in second output file } mismatched end bracket should be ignored >
end junk
Результирующий файл вывод / 0001.txt:
{ here is the actual payload
more payload
'atomic payload { with start bracket that should be ignored'
nested start bracket { - all of this line is untouchable payload too
here is more payload
"this atomic has a literal double-quote \" inside"
"yet more atomic payload; this one's got a smiley ;-) and a heart <3"
end of nested bracket pair } - all of this line is untouchable payload too
this is payload too
"here's a totally unprovoked $ sign and an * asterisk"
}
... и получившийся файл вывод / 0002.txt:
<
payload that goes in second output file } mismatched end bracket should be ignored >
Спасибо @dawg за помощь :)
Я не собираюсь использовать это ни для чего, кроме личных средств, но я понимаю, что вы имеете в виду @MatiasBarrios. Если вы считаете, что мой вопрос идет вразрез с какой-либо хорошей практикой или правилом сообщества, я прошу прощения и подумаю о том, чтобы отозвать его.