Суммирование столбца чисел в текстовом файле с использованием Perl
Хорошо, поэтому я очень новичок в Perl. У меня есть текстовый файл, а в файле есть 4 столбца данных (дата, время, размер файлов, файлов). Мне нужно создать небольшой script, который может открыть файл и получить средний размер файлов. Я читал так много онлайн, но я все еще не могу понять, как это сделать. Это то, что у меня есть до сих пор, но я не уверен, что я даже близко делаю это правильно.
#!/usr/bin/perl
open FILE, "files.txt";
#@array = File;
while(FILE){
#chomp;
($date, $time, $numbers, $type) = split(/ /,<FILE>);
$total += $numbers;
}
print"the total is $total\n";
Так выглядят данные в файле. Это лишь некоторые из них. Мне нужно получить числа в третьем столбце.
12/02/2002 12:16 AM 86016 a2p.exe
10/10/2004 11:33 AM 393 avgfsznew.pl
11/01/2003 04:42 PM 38124 c2ph.bat
Ответы
Ответ 1
Ваша программа достаточно близка к работе. С этими изменениями он будет делать именно то, что вы хотите
-
Всегда используйте use strict
и use warnings
в начале вашей программы и объявляйте все свои переменные с помощью my
. Это поможет вам найти много простых ошибок, которые вы могли бы игнорировать.
-
Используйте лексические файловые дескрипторы, трехпараметрическую форму open
и всегда проверяйте статус возврата любого вызова open
-
Объявить переменную $total
вне цикла. Объявление внутри цикла означает, что он будет создан и уничтожен каждый раз вокруг цикла, и он не сможет скопировать общий
-
Объявить переменную $count
таким же образом. Он понадобится для вычисления среднего значения
-
Используя while (FILE) {...}
просто тесты, которые FILE
истинны. Вам нужно прочитать это вместо этого, поэтому вы должны использовать оператор readline
, например <FILE>
-
Вы хотите, чтобы вызов по умолчанию был split
(без каких-либо параметров), который вернет все непространственные поля в $_
в виде списка
-
Вам нужно добавить переменную в присваивание, чтобы разрешить использовать поля athe AM
или PM
в каждой строке
Вот модификация вашего кода, который отлично работает
use strict;
use warnings;
open my $fh, '<', "files.txt" or die $!;
my $total = 0;
my $count = 0;
while (<$fh>) {
my ($date, $time, $ampm, $numbers, $type) = split;
$total += $numbers;
$count += 1;
}
print "The total is $total\n";
print "The count is $count\n";
print "The average is ", $total / $count, "\n";
Выход
The total is 124533
The count is 3
The average is 41511
Ответ 2
Заманчиво использовать Perl awk
-подобный вариант автоматического разделения. Есть 5 столбцов; три, содержащие информацию о дате и времени, затем размер и имя.
Первая версия script, которую я написал, также является самой многословной:
perl -n -a -e '$total += $F[3]; $num++; END { printf "%12.2f\n", $total / ($num + 0.0); }'
Параметр -a
(авторасширение) разбивает строку на пробел в массив @F
. В сочетании с опцией -n
(которая заставляет Perl работать в цикле, который поочередно считывает аргументы имени файла или стандартный ввод, без печати каждой строки), код добавляет $F[3]
(четвертый столбец, считая от 0) до $total
, который автома- тически инициализируется нулем при первом использовании. Он также подсчитывает строки в $num
. Блок END
выполняется, когда считывается все входные данные; он использует printf()
для форматирования значения. + 0.0
гарантирует, что арифметика выполняется с плавающей точкой, а не с целочисленной арифметикой. Это очень похоже на awk
script:
awk '{ total += $4 } END { print total / NR }'
Первые проекты программ редко бывают оптимальными - или, по крайней мере, я не так хорош программист. Помощь по пересмотру.
Perl был разработан, в частности, как убийца awk
. По-прежнему существует программа a2p
, распределенная с Perl для преобразования скриптов awk
в Perl (а также s2p
для преобразования скриптов sed
в Perl). И Perl имеет автоматическую (встроенную) переменную, которая отслеживает количество прочитанных строк. У него несколько имен. Терстером является $.
; мнемоническое имя $NR
доступно, если вы use English;
в script; так что $INPUT_LINE_NUMBER
. Таким образом, использование $num
не требуется. Также выясняется, что Perl все равно выполняет разделение с плавающей запятой, поэтому часть + 0.0
не нужна. Это приводит к следующим версиям:
perl -MEnglish -n -a -e '$total += $F[3]; END { printf "%12.2f\n", $total / $NR; }'
или
perl -n -a -e '$total += $F[3]; END { printf "%12.2f\n", $total / $.; }'
Вы можете настроить формат печати в соответствии с вашими прихотями и фантазиями. Это, по существу, script я буду использовать в долгосрочной перспективе; это довольно ясно, без каких-либо причин. script можно разделить на несколько строк, если хотите. Задача достаточно простая, чтобы разборчивость одной строки не была проблемой, IMNSHO. И красота этого заключается в том, что вам не нужно futz с split
и массивов и читать петли самостоятельно; Perl делает большую часть этого для вас. (Конечно, он взорвется на пустом входе, это исправление тривиально, см. Ниже.)
Рекомендуемая версия
perl -n -a -e '$total += $F[3]; END { printf "%12.2f\n", $total / $. if $.; }'
if $.
проверяет, является ли число прочитанных строк нулевым или нет; printf
и деление опущены, если $.
равно нулю, поэтому script ничего не выводит при отсутствии ввода.
Существует благородная (или неблагородная) игра под названием "Code Golf", которую играли в первые дни Stack Overflow, но вопросы Code Golf больше не считаются хорошими. Цель Code Golf - написать программу, которая выполняет определенную задачу как можно меньше символов. Вы можете играть в Code Golf с этим и сжимать его еще дальше, если вы не слишком беспокоитесь о формате вывода и используете хотя бы Perl 5.10:
perl -Mv5.10 -n -a -e '$total += $F[3]; END { say $total / $. if $.; }'
И, очевидно, в нем много лишних пробелов и букв:
perl -Mv5.10 -nae '$t+=$F[3];END{say$t/$.if$.}'
Это не так ясно, как рекомендуемая версия.
Ответ 3
#!/usr/bin/perl
use warnings;
use strict;
open my $file, "<", "files.txt";
my ($total, $cnt);
while(<$file>){
$total += (split(/\s+/, $_))[3];
$cnt++;
}
close $file;
print "number of files: $cnt\n";
print "total size: $total\n";
printf "avg: %.2f\n", $total/$cnt;
Или вы можете использовать awk
:
awk '{t+=$4} END{print t/NR}' files.txt
Ответ 4
Попробуйте сделать это:
#!/usr/bin/perl -l
use strict; use warnings;
open my $file, '<', "my_file" or die "open error [$!]";
my ($total, $count);
while (<$file>){
chomp;
next if /^$/;
my ($date, $time, $x, $numbers, $type) = split;
$total += $numbers;
$count++;
}
print "the average is " . $total/$count . " and the total is $total";
close $file;
Ответ 5
Это решение открывает файл и проходит через каждую строку файла. Затем он разбивает файл на пять переменных в строке путем разделения на 1 или более пробелов.
- откройте файл для чтения,
"<"
, и если он сбой, поднимите сообщение об ошибке or die "..."
-
my ($total, $cnt)
- общее количество столбцов и количество добавленных файлов
-
while(<FILE>) { ... }
проходит через каждую строку файла с помощью дескриптора файла и сохраняет строку в $_
-
chomp
удаляет разделитель входных данных в $_
. В unix разделитель по умолчанию представляет собой новую строку \n
-
split(/\s+/, $_)
Разделяет текущую строку, представленную $_
, с разделителем \s+
. \s
представляет собой пробел, +
после этого означает "1 или более". Итак, мы разделили следующую строку на 1 или более пробелов.
-
Затем мы обновляем $total
и $cnt
#!/usr/bin/perl
open FILE, "<", "files.txt" or die "Error opening file: $!";
my ($total, $cnt);
while(<FILE>){
chomp;
my ($date, $time, $am_pm, $numbers, $type) = split(/\s+/, $_);
$total += $numbers;
$cnt++;
}
close FILE;
print"the total is $total and count of $cnt\n";`
Ответ 6
Это так просто:
perl -F -lane '$a+=$F[3];END{print "The average size is ".$a/$.}' your_file
проверено ниже:
> cat temp
12/02/2002 12:16 AM 86016 a2p.exe
10/10/2004 11:33 AM 393 avgfsznew.pl
11/01/2003 04:42 PM 38124 c2ph.bat
Теперь выполнение:
> perl -F -lane '$a+=$F[3];END{print "The average size is ".$a/$.}' temp
The average size is 41511
>
Объяснение:
-F -a говорит, что сохраняет строку в формате массива. С разделителем по умолчанию в качестве пробела или вкладки.
поэтому nopw $F [3] имеет размер файла.
суммируйте все размеры в 4-м столбце до тех пор, пока все строки не будут обработаны.
END будет выполнен после обработки всех строк в файле.
so $. в конце будет указано количество строк.
поэтому $a/$. даст среднее значение.