против моего лучшего суждения я постараюсь помочь вам снова.
Проблема заключается не в том, как найти пустую строку. Проблема заключается не в том, какое регулярное выражение использовать. Основная проблема заключается в понимании того, как анализировать проблему и превращать этот анализ в код.
В этом случае проблема заключается в «Как разобрать этот формат?»
Я написал для вас парсер. Я также нашел время, чтобы написать подробное описание процесса, который я использовал для его написания.
ВНИМАНИЕ: анализатор не подвергается тщательной проверке на всех случаях. У него недостаточно встроенной обработки ошибок. Для этих функций вы можете запросить тарифную карту или написать ее самостоятельно.
Вот выборка данных вы предоставили (я не уверен, какой из нескольких вопросов, которые я вытащил это из):
constant fixup GemEstabCommDelay = <U2 20>
vid = 6
name = "ESTABLISHCOMMUNICATIONSTIMEOUT"
units = "s"
min = <U2 0>
max = <U2 1800>
default = <U2 20>
constant fixup private GemConstantFileName = <A "C:\\TMP\\CONST.LOG">
vid = 4
name = "" units = ""
constant fixup private GemAlarmFileName = <A "C:\\TMP\\ALARM.LOG">
vid = 0
name = ""
units = ""
Перед тем, как написать парсер для файла данных, вы должны иметь описать структуру файла. Если вы используете стандартный формат (например, XML), вы можете прочитать существующую спецификацию. Если вы используете какой-то домашний формат, вы можете написать его самостоятельно.
Итак, на основе выборочных данных, мы можем видеть, что:
- данные разбиваются на блоки.
- каждый блок начинается со слова
constant
в графе 0.
- каждый блок заканчивается пустой линией.
- блок состоит из стартовой линии и нулевых или более дополнительных строк.
- Стартовая строка состоит из ключевого слова
constant
, за которым следует одно или несколько слов с разделителями пробелов, знак '=' и значение данных, приведенное в качестве значения <>
.
- Последнее ключевое слово представляет собой имя константы.Назовите его
constant_name
- Данные, полученные с помощью
<>
, представляются комбинированным спецификатором типа/значения.
- Предыдущие ключевые слова указывают, что указаны дополнительные метаданные о константе. Назовем их
options
.
- Дополнительные строки указывают дополнительные пары значений ключа. Назовем их атрибутами. Атрибуты могут иметь одно значение или у них может быть спецификатор типа/значения.
- Один или несколько атрибутов могут отображаться в одной строке.
Хорошо, теперь у нас есть приблизительная спецификация. Что нам с этим делать?
Как структурирован формат? Рассмотрим логические единицы организации от самых больших до самых маленьких. Они будут определять структуру и поток нашего кода.
- ФАЙЛ сделан из БЛОКОВ.
- БЛОКИ ИЗ ЛИНИЙ.
Поэтому наш парсер должен разложить файл на блоки, а затем обработать блоки.
Теперь мы шероховатый из парсер в комментариях:
# Parse a constant spec file.
# Until file is done:
# Read in a whole block
# Parse the block and return key/value pairs for a hash.
# Store a ref to the hash in a big hash of all blocks, keyed by constant_name.
# Return ref to big hash with all block data
Теперь мы начинаем заполнять некоторый код:
# Parse a constant spec file.
sub parse_constant_spec {
my $fh = shift;
my %spec;
# Until file is done:
# Read in a whole block
while(my $block = read_block($fh)) {
# Parse the and return key/value pairs for a hash.
my %constant = parse_block($block);
# Store a ref to the hash in a big hash of all blocks, keyed by constant_name.
$spec{ $constant{name} } = \%constant;
}
# Return ref to big hash with all block data
return \%spec;
}
Но это не будет работать. На данный момент еще не написаны сообщения parse_block
и read_block
. На этом этапе все в порядке. Дело в грубых чертах в маленьких, понятных кусках. Время от времени, чтобы сохранить читаемость, вам нужно замаскировать детали в подпрограмме - иначе вы столкнетесь с чудовищными тысячами линейных подсетей, которые невозможно отладить.
Теперь мы знаем, что нужно написать пару сабвуферов, чтобы закончить, и др альт:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $fh = \*DATA;
print Dumper parse_constant_spec($fh);
# Parse a constant spec file.
# Pass in a handle to process.
# As long as it acts like a file handle, it will work.
sub parse_constant_spec {
my $fh = shift;
my %spec;
# Until file is done:
# Read in a whole block
while(my $block = read_block($fh)) {
# Parse the and return key/value pairs for a hash.
my %constant = parse_block($block);
# Store a ref to the hash in a big hash of all blocks, keyed by constant_name.
$spec{ $constant{const_name} } = \%constant;
}
# Return ref to big hash with all block data
return \%spec;
}
# Read a constant definition block from a file handle.
# void return when there is no data left in the file.
# Otherwise return an array ref containing lines to in the block.
sub read_block {
my $fh = shift;
my @lines;
my $block_started = 0;
while(my $line = <$fh>) {
$block_started++ if $line =~ /^constant/;
if($block_started) {
last if $line =~ /^\s*$/;
push @lines, $line;
}
}
return \@lines if @lines;
return;
}
sub parse_block {
my $block = shift;
my ($start_line, @attribs) = @$block;
my %constant;
# Break down first line:
# First separate assignment from option list.
my ($start_head, $start_tail) = split /=/, $start_line;
# work on option list
my @options = split /\s+/, $start_head;
# Recover constant_name from options:
$constant{const_name} = pop @options;
$constant{options} = \@options;
# Now we parse the value/type specifier
@constant{'type', 'value' } = parse_type_value_specifier($start_tail);
# Parse attribute lines.
# since we've already got multiple per line, get them all at once.
chomp @attribs;
my $attribs = join ' ', @attribs;
# we have one long line of mixed key = "value" or key = <TYPE VALUE>
@attribs = $attribs =~ /\s*(\w+\s+=\s+".*?"|\w+\s+=\s+<.*?>)\s*/g;
for my $attrib (@attribs) {
warn "$attrib\n";
my ($name, $value) = split /\s*=\s*/, $attrib;
if($value =~ /^"/) {
$value =~ s/^"|"\s*$//g;
}
elsif($value =~ /^</) {
$value = [ parse_type_value_specifier($start_tail) ];
}
else {
warn "Bad line";
}
$constant{ $name } = $value;
}
return %constant;
}
sub parse_type_value_specifier {
my $tvs = shift;
my ($type, $value) = $tvs =~ /<(\w+)\s+(.*?)>/;
return $type, $value;
}
__DATA__
constant fixup GemEstabCommDelay = <U2 20>
vid = 6
name = "ESTABLISHCOMMUNICATIONSTIMEOUT"
units = "s"
min = <U2 0>
max = <U2 1800>
default = <U2 20>
constant fixup private GemConstantFileName = <A "C:\\TMP\\CONST.LOG">
vid = 4
name = "" units = ""
constant fixup private GemAlarmFileName = <A "C:\\TMP\\ALARM.LOG">
vid = 0
name = ""
units = ""
Приведенный выше код далек от совершенства. IMO, parse_block
слишком длинный и должен быть разбит на более мелкие субмарины. Кроме того, почти недостаточно проверки и обеспечения корректного ввода. Имена переменных и описания могут быть более четкими, но я не понимаю семантики вашего формата данных. Более лучшие имена будут более точно соответствовать семантике формата данных.
Несмотря на эти проблемы, он анализирует ваш формат и создает большую удобную структуру данных, которая может быть заполнена любым желаемым форматом вывода.
Если вы используете этот формат во многих местах, я рекомендую поместить код синтаксического анализа в модуль. См. perldoc perlmod для получения дополнительной информации.
Теперь прекратите использовать глобальные переменные и игнорируйте полезные советы. Пожалуйста, начните читать perldoc, прочитайте «Изучение Perl и Perl Best Practices», используйте строгие предупреждения об использовании. Пока я читаю списки чтения, пройдите Global Variables are Bad, а затем побродите по вики, чтобы читать и учиться. Я узнал больше о написании программного обеспечения, прочитав c2, чем в школе.
Если у вас есть вопросы о том, как работает этот код, почему он изложен как есть, то какие другие варианты могли бы быть сделаны, выскажитесь и спросите. Я готов помочь готовому ученику.
Ваш английский хороший, но ясно, что вы не носитель языка. Возможно, я использовал слишком много сложных предложений. Если вам нужны части этого написания в простых предложениях, я могу попытаться помочь. Я понимаю, что работать на иностранном языке очень сложно.
Почему вы все еще делаете все неправильные вещи, которые я, Шверн и другие указывали в ваших предыдущих вопросах? http://stackoverflow.com/questions/1945538/how-do-i-extract-and-parse-quoted-strings-in-perl http://stackoverflow.com/questions/1919232/how-can-i-write -xml-data-to-a-file-with-perl Вы даже не заменили неправильное использование 'printf'' 'print'! Мне жаль бедняков, которые должны поддерживать этот код. Я плохо себя чувствую для тех, кто платит за это тоже. Разве вы не поняли суть этих предложений? – daotoad
Действительно, в этом коде есть так много WTF, что трудно пробраться через них, чтобы определить * актуальный * вопрос, встроенный в него. Использование глобальных переменных вместо передачи аргументов функции; неправильное использование 'printf'; нумерованные переменные, а не разумные имена (или массив); огромные горные предложения 'if', которые должны обрабатываться переключателем или отдельными функциями; это продолжается и продолжается. – Ether
@daotoad, СПАСИБО за ваш комментарий. На самом деле, я никогда не учился perl раньше. Это мой первый проект (я не знал различий между «printf» и «print»). Я нашел еще один скрипт perl, который написал мой стиль. Вот почему я публиковал снова и снова. Я уже признал преимущество предложения Шверна, и ваш учебник по деталям помог мне много актуален. Я бы сменил свой дизайн с вашим гидом. Очень сложно двигаться на основе моего огромного и сложного текстового файла, кстати, я мог понять, почему я ДОЛЖЕН изменить свой подход к вашему методу предложения. , :-) –