#perl #parsing #text
#perl #синтаксический анализ #текст
Вопрос:
Я новичок в программировании на perl и хотел бы узнать о разборе текстовых файлов с помощью perl. У меня есть текстовый файл с неправильным форматированием, и я хотел бы разобрать его на три части.
В основном файл содержит текст, подобный этим:
;out;asoljefsaiouerfas'pozsirt'z
mysql_query("SELECT * FROM Table WHERE (value='true') OR (value2='true') OR (value3='true') ");
1234 434 3454
4if[9put[e]9sd=09q]024s-q]3-=04i
select ta.somefield, tc.somefield
from TableA ta INNER JOIN TableC tc on tc.somefield=ta.somefield
INNER JOIN TableB tb on tb.somefield=ta.somefield
ORDER by tb.somefield
234 4536 234
и список можно продолжить с этим форматом.
Итак, что мне нужно сделать, это разобрать его на три части. А именно тот, что сверху, получает проверки хэша. Вторым является запрос mysql, а третьим будет синтаксический анализ трех чисел. По какой-то причине я не понимаю, как это сделать. Я использую функцию ‘open’ в perl для получения данных из текстового файла. И затем я пытаюсь использовать функцию ‘split’ для разрывов строк, но оказывается, что запросы не находятся в одной строке или в шаблоне, поэтому я не могу использовать его таким образом, как я понял.
Комментарии:
1. Кто-то «разработал» этот формат файла — я предлагаю вам заключить с ним контракт.
2. Я думаю, часть вашей проблемы заключается в том, что вы воображаете, что вам нужно прочитать весь файл перед синтаксическим анализом. В Perl есть отличные инструменты, которые помогут вам справиться с построчной обработкой, которые вы можете использовать для решения этой проблемы. 🙂
Ответ №1:
Предположения:
- Между блоками данных будет пустая строка.
- Эта пустая строка будет состоять только из новой строки.
- В этих фрагментах проверки хэша будут в одной верхней строке, а три числа будут в одной нижней строке.
имея это в виду:
use strict;
use warnings;
use English qw<$RS $OS_ERROR>;
local $RS = "nn";
open( my $fh, '<', $path_to_file )
or die "Could not open $path_to_file! - $OS_ERROR"
;
while ( <> ) {
chomp;
my ( $hash_check_line
, @inner_lines
)
= split /n/
;
my @numbers = split /D /, pop @inner_lines;
my $sql = join( "n", @inner_lines );
...
}
Изменяя $RS
( $/
или $INPUT_RECORD_SEPARATOR
) на двойной перевод строк, мы меняем способ чтения записей.
Это не так уж и странно, но за годы работы с Perl мне приходилось использовать разделитель записей для некоторых довольно интересных строк, но иногда достаточно прочитать только тот фрагмент, который вы хотите прочитать.
Комментарии:
1. 1: Иногда вам просто нужно начать с изменения разделителя входных записей, если вы не хотите иметь дело с полным безумием . 🙂
2. Сначала я сопротивлялся идее изменения $ RS (потому что я ненавижу менять эти переменные, если это вообще возможно), но … ну, это разделитель записей. Для меня это имеет смысл. 🙂
3. Во всяком случае, я бы внес изменения в $ RS local, чтобы это не повлияло на всю вашу программу. 🙂
Ответ №2:
О, о БОЖЕ.
Алгоритм, который я вижу, таков:
- Кэшируйте первую строку.
- Прочтите все строки, пока не останется пустая строка.
- «Последней» строкой будут цифры.
- Все остальное будет запросом.
Имея это в виду, я представляю следующий код:
open my $fh, '<', $path_to_file
or die "Can't open $path_to_file: $!";
while (my ($checksum, $query, $numbers) = read_record($fh) ) {
# do something with record
}
close $fh or warn "$!";
sub read_record {
my $fh = shift;
my @lines;
LINE: while (my $line = <$fh>) {
chomp $line;
last LINE if $line eq q{}; # if empty, we're done with the record!
push @lines, $line; # store it :)
}
return unless @lines; # if we didn't get anything, eof!
my $checksum = shift @lines; # first was checksum.
my $numbers = pop @lines; # last thing read was numbers.
my $query = join ' ', @lines; # everything else, query.
return ($checksum, $query, $numbers);
}
Модифицируйте, конечно, в соответствии с граничными условиями.
Ответ №3:
Кажется, работает следующее:
while ($file_content =~ /s*^(. ?)^(.*?)^(d s d s d )$/smg) {
my $checksum = $1;
my $query = $2;
my $numbers = $3;
# do stuff
}
Вот объяснение для регулярного выражения:
s* # eat up empty lines
^(. ?) # save the checksum line to group 1
^(. ?) # save one or multiple query lines to group 2
^(d s d s d )$ # save number line to group 3
Первая группа всегда будет состоять только из одной строки, так как она ленива, когда встречается следующая строка, регулярное выражение попытается начать сопоставление со второй группой. В этот момент, если остальная часть сопоставления может быть завершена, эта вторая группа будет содержать все последующие строки перед числами.
Комментарии:
1. Что за черт? Я не могу прочитать это регулярное выражение… Могу ли я предложить вам
qr
режим использования регулярных выражений/x
и предоставить хорошие пробелы и комментарии? (Я тоже не совсем новичок в Perl. Я знаю, что регулярные выражения иногда бывает трудно читать. Но я привожу комментарии для ясности, и это помогает, когда мне нужно поддерживать это в дальнейшем!)2. @Platinum Azure сейчас работает над прокомментированной версией, просто хотел сначала получить ответ.
3. Кроме того, это будет медленно . Однако вы могли бы получить небольшой прирост производительности, используя жадное сопоставление во второй группе захвата.
4. Если вторая группа захвата жадная, то она будет совпадать до самой последней строки с тремя цифрами. Я знаю, что это не так эффективно, как другие решения, но это также не делает никаких предположений о том, что содержит файл, поэтому OP может счесть это полезным, если есть фрагменты файла, которые не в формате контрольной суммы, запроса, чисел.
5. Он не будет совпадать до последней строки, потому что тогда следующая группа захвата ничего не будет захватывать, и это совпадение завершится неудачей, поэтому механизму регулярных выражений потребуется выполнить обратный поиск строки. Это лучше, чем получать только одну строку во второй группе, затем видеть, что вторая строка (которая, скажем, является 2-й из 3 строк в SQL) не соответствует шаблону чисел, затем «forward-tracking» (если хотите), чтобы взять 2 строки SQL, видя, что третья строка не соответствует числам, «forward-tracking», чтобы, наконец, взять все три строки… Представьте, что произойдет, если у вас есть инструкция SQL из 10, 20 или 100 строк!