Как узнать, разделен ли файл табуляцией или пробелом в Perl?

Я загружаю файл в программу Perl с HTML-страницы. После того, как файл был загружен, я хочу определить, является ли файл разделителем пробела или табуляции, и все значения являются целыми числами. Если это не так, я хочу вывести какое-то сообщение.

Я думал прочитать каждый символ файла и проверить, является ли он целым числом. Если это не удастся, я покажу выходное сообщение. Есть лучший способ это сделать?

Я проверил несколько примеров и могу прочитать весь файл построчно, но как я могу прочитать каждый символ в этой строке? Должен ли я разбиваться на space или tab, поскольку файл может быть любым?


person Community    schedule 30.03.2009    source источник
comment
Если вы яснее о требованиях, вы получите лучшие ответы. Линии имеют значение? Может ли число разбиваться на строки или \n считается разделителем? Схлопываются ли несколько разделителей? Могут ли \t и пробелы сосуществовать в файле? Каков желаемый результат от анализа файла - список целых чисел?   -  person daotoad    schedule 31.03.2009


Ответы (7)


Достаточно просто разделить на оба пробелы и вкладки:

my @fields = split /[ \t]/, $line;

но если это должно быть только одно или другое, и вы не знаете, какое заранее, это немного сложнее. Если вы знаете, сколько столбцов должно быть во входных данных, вы можете попробовать подсчитать количество пробелов и количество табуляций в каждой строке и посмотреть, есть ли правильное количество разделителей. Например. если должно быть 5 столбцов и вы видите 4 табуляции в каждой строке, это хорошая ставка на то, что пользователь использует табуляцию в качестве разделителей. Если ни один из них не совпадает, вернуть ошибку.

Проверка целочисленных значений проста:

for my $val ( @fields ) {
    die "'$val' is not an integer!" if $val !~ /^-?\d+$/;
}
person wisnij    schedule 30.03.2009
comment
Я не думаю, что OP должен иметь значение, смешал ли кто-то пробелы и вкладки в файле. Кажется, что это добавит много головной боли с очень небольшой пользой. - person Chris Lutz; 31.03.2009

звучит так, будто не имеет значения, разделены ли они пробелами или табуляцией. В какой-то момент вам придется прочитать все символы файла, чтобы проверить их и проанализировать. Зачем делать эти два шага. Потребляйте целые числа из файла, пока не столкнетесь с чем-то, что не является пробелом или допустимым целым числом, затем пожалуйтесь (и, возможно, откатитесь)

person SingleNegationElimination    schedule 30.03.2009

Я загружаю файл в программу perl с html-страницы. После того, как файл был загружен, я хочу определить, является ли файл (разделителем пробела или табуляции), и все значения являются целыми числами. Если это не так, я хочу вывести какое-то сообщение.

Это условие означает, что ваши данные должны содержать только цифры, пробел и символы табуляции (в основном это должны быть цифры и пробел или только цифры и табуляция).

Для этого просто загрузите данные в переменную и проверьте, совпадают ли они:

$data =~ /\A[0-9 \t]+\z/;

Если он совпадает - это будет означать, что у вас будет набор целых чисел, разделенных пробелами или табуляциями (на самом деле не имеет значения, какой символ использовался для разделения целых чисел).

Если вашим следующим шагом будет извлечение этих целых чисел (что звучит логично), вы можете легко сделать это:

@integers = split /[ \t]+/, $data;

or

@integers = $data =~ /(\d+)/g;
person Community    schedule 30.03.2009
comment
Вы рассматриваете только положительные целые числа в своих регулярных выражениях. Кроме того, если включить '-', ваше первое регулярное выражение больше не будет правильно проверять ввод 12\t1-3\t4, хотя ввод недействителен. - person user55400; 31.03.2009

Чтобы добавить к ответу, я напишу четкий и простой. Эта версия:

  1. использует только самые основные функции и конструкции Perl, поэтому любой, кто хотя бы немного знает Perl, должен довольно быстро освоить его. Не для того, чтобы обидеть или что-то в этом роде, и нет ничего постыдного в том, чтобы быть новичком - я просто пытаюсь написать что-то, что вы сможете понять, независимо от вашего уровня навыков.
  2. принимает табуляцию или пробелы в качестве разделителя, что позволяет их свободно смешивать. В закомментированном коде будет подробно описан тривиальный способ принудительного выполнения операции «или-или» во всем документе.
  3. печатает хорошие сообщения об ошибках, когда обнаруживает неверные значения. Должно показывать недопустимое значение и строку, в которой оно появилось.
  4. позволяет обрабатывать данные так, как вам нравится. Я не собираюсь хранить его в массиве или чем-то еще, просто поместите ... в одну точку, и туда вы добавите немного кода, чтобы выполнить любую обработку данных в данной строке, которую вы хотите выполнить.

Итак, вот:

use strict;
use warnings;

open(my $data, "<", $filename);
# define $filename before this, or get it from the user

my $whitespace = "\t ";

chomp(my @data = <$data>);

# check first line for whitespace to enforce...
#if($data[0] =~ /\t/ and $data[0] !~ / /) {
#  $whitespace = "\t";
#} elsif($data[0] =~ / / and $data[0] !~ /\t/) {
#  $whitespace = " ";
#} else {
#  warn "Warning: mixed whitespace on line 1 - ignoring whitespace.\n";
#}

foreach my $n (0 .. $#data) {
  my @fields = split(/[$whitespace]+/, $data[$n]);
  foreach my $f (@fields) {
    if($f !~ /-?\d/) { # \D will call "-12" invalid
      if($f =~ /\s/) {
        warn "Warning: invalid whitespace use at line $n - ignoring.\n";
      } else {
        warn "Warning: invalid value '$f' at line $n - ignoring.\n";
      }
    } else {
      ... # do something with $f, or...
    }
  }
  ... # do something with @fields if you want to process the whole list
}

Есть лучшие, более быстрые, более компактные и, возможно, даже более читаемые (в зависимости от того, кого вы спросите) способы сделать это, но этот использует самые основные конструкции, и любой программист Perl должен быть в состоянии прочитать это, независимо от уровня навыков. (хорошо, если вы только начинаете использовать Perl в качестве первого языка, вы можете ничего из этого не знать, но тогда вам пока не следует пытаться делать что-то подобное).

РЕДАКТИРОВАТЬ: исправлено мое регулярное выражение для сопоставления целых чисел. Раньше это было лениво и допускало «12-4», что, очевидно, не является целым числом (хотя оно оценивается как единица, но это намного сложнее (ну, не совсем, но это не то, что хочет ОП (или это? было бы забавно (ВСТАВЬТЕ ЗДЕСЬ ШУТКУ НА LISP)))). Спасибо wisnij - я рад, что перечитал ваш пост, так как вы написали регулярное выражение лучше, чем я.

person Chris Lutz    schedule 31.03.2009

Ваш вопрос не очень ясен. Похоже, вы ожидаете, что данные будут в этом формате:

123 456 789
234 567 890

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

/^\d+(\s+\d+)*$/

Если могут быть отрицательные числа, используйте это вместо этого:

/^-?\d+(\s+-?\d+)*$/

Ваше регулярное выражение не будет соответствовать пустой строке, и это тоже не будет. Наверное, так и должно быть; Я ожидаю, что пустые строки (включая строки, содержащие только пробелы) будут запрещены в подобном случае. Однако в конце файла может быть одна или несколько пустых строк. Это означает, что как только вы найдете строку, которая не соответствует приведенному выше регулярному выражению, вы должны убедиться, что каждая из оставшихся строк имеет нулевую длину.

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

person Alan Moore    schedule 31.03.2009

Вы можете просто использовать регулярное выражение. Вот чем славится Perl ;-).

Простой пример:

perl -ne 'if ($_=~/^(\d+\s+)+$/){print "yep\n";}'

будет принимать только строки, содержащие только цифры и пробелы. Это должно вас завести.

person sleske    schedule 30.03.2009
comment
Я не силен в регулярных выражениях. не могли бы вы объяснить это выражение? - person ; 31.03.2009
comment
также, как можно изменить это регулярное выражение, чтобы оно работало и в последней строке файла. он никогда не проходит последнюю строку файла. может быть из-за характера eof? - person ; 31.03.2009
comment
я изменил его на: ~/^(\d+\s*)+?$/ который, кажется, работает, но выглядит нормально - person ; 31.03.2009
comment
Извините, сложно объяснить регулярные выражения в 300 символов. Но вы действительно, ДЕЙСТВИТЕЛЬНО должны изучить основы, это необходимо для обработки текста. См., например. perlretut на справочных страницах/документации Perl. И для меня решение также работает для последней строки файла. Странный... - person sleske; 31.03.2009
comment
Насколько я могу судить, ваше модифицированное регулярное выражение должно делать то же самое, что и мое. Для какого входа он дает другой результат? - person sleske; 31.03.2009
comment
@sleske, ваше регулярное выражение требует, чтобы каждая строка заканчивалась одним или несколькими пробелами или табуляцией. Версия OP делает конечный пробел необязательным. - person Alan Moore; 31.03.2009
comment
Для справки, я думаю, что лучше избегать ответов, специфичных для регулярных выражений и командной строки, в таком вопросе для начинающих, как этот. OP (без обид) кажется немного новым в Perl, и использование чего-то подобного превратит его / ее в Python. :П - person Chris Lutz; 31.03.2009
comment
@Alan M: Плохо, ты прав. Не должен отвечать, когда я устал. Я проглядел * против + :-(. - person sleske; 31.03.2009
comment
@Chris Lutz: Для справки, я думаю, что любой достойный программист должен теперь знать основы регулярных выражений :-). Это слишком важный инструмент, чтобы его упустить, особенно. в Перл. Но я думаю, что здесь у нас есть лучшее из обоих: решения с регулярными выражениями и без них, поэтому OP может выбирать. - person sleske; 31.03.2009
comment
Также обратите внимание, что ответ wisnij, получивший наибольшее количество голосов, использует регулярное выражение (внутри разделения) ;-). - person sleske; 31.03.2009

Я предполагаю несколько вещей о вашем формате и желаемых результатах.

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

Кроме того, мой код принимает любые пробелы в качестве разделителя.

use strict;
use warnings;

# Slurp whole file into a scalar.
my $file_contents;
{   local $/;
    $/ = undef;
    $file_contents = <DATA>;
}

# Extract and validate numbers
my @ints = grep validate_integer($_), 
                split( /\s+/, $file_contents ); 
print "@ints\n";


sub validate_integer {
    my $value = shift;

    # is it an integer?
    # add additional validation here.
    if( $value =~ /^-?\d+$/ ) {
        return 1;
    }

    # die here if you want a fatal exception.
    warn "Illegal value '$value'\n";
    return;
}

__DATA__
1 -2 3 4
5 8.8
-6
    10a b c10 -99-
    8   9 98- 9-8
10 -11  12  13

Это приводит к:

Illegal value '8.8'
Illegal value '10a'
Illegal value 'b'
Illegal value 'c10'
Illegal value '-99-'
Illegal value '98-'
Illegal value '9-8'
1 -2 3 4 5 -6 8 9 10 -11 12 13

Обновления:

  • Исправлена ​​обработка отрицательных чисел.
  • Заменена проверка map на grep.
  • Переключено на split вместо захвата без пробелов из re.

Если вы хотите обработать файл построчно, вы можете заключить grep в цикл, который читает файл.

person daotoad    schedule 31.03.2009