Как подсчитать символы, слова и строки в файле, используя Perl?
Что является хорошим/лучшим способом подсчета количества символов, слов и строк текстового файла с использованием Perl (без использования wc)?
Ответы
Ответ 1
Здесь код perl. Подсчет слов может быть несколько субъективным, но я просто говорю, что любая строка символов не является пробелом.
open(FILE, "<file.txt") or die "Could not open file: $!";
my ($lines, $words, $chars) = (0,0,0);
while (<FILE>) {
$lines++;
$chars += length($_);
$words += scalar(split(/\s+/, $_));
}
print("lines=$lines words=$words chars=$chars\n");
Ответ 2
Вариант ответа bmdhacks, который, вероятно, даст лучшие результаты, заключается в использовании \s + (или даже лучше \W +) в качестве разделителя. Рассмотрим строку "Быстрая коричневая лиса" (дополнительные пробелы, если она не очевидна). Использование разделителя одного символа пробела даст число слов шесть не четыре. Итак, попробуйте:
open(FILE, "<file.txt") or die "Could not open file: $!";
my ($lines, $words, $chars) = (0,0,0);
while (<FILE>) {
$lines++;
$chars += length($_);
$words += scalar(split(/\W+/, $_));
}
print("lines=$lines words=$words chars=$chars\n");
Использование\W + в качестве разделителя прекратит пунктуацию (между прочим) от подсчета слов.
Ответ 3
Word Count tool подсчитывает символы, слова и строки в текстовых файлах
Ответ 4
Здесь. Попробуйте эту юкокодированную версию программы wc.
-
Он пропускает нефайловые аргументы (каналы, каталоги, сокеты и т.д.).
-
Он принимает текст UTF-8.
-
Он подсчитывает любые пробелы Unicode в качестве разделителя слов.
-
Он также принимает альтернативные кодировки, если в конце имени файла есть .ENCODING
, например foo.cp1252
, foo.latin1
, foo.utf16
и т.д.
-
Он также работает с файлами, которые были сжаты в различных форматах.
-
Он дает количество абзацев, строк, слов, графем, символов и байтов.
-
Он понимает все последовательности очередей Unicode.
-
Он предупреждает о поврежденных текстовых файлах с ошибками linebreak.
Вот пример его запуска:
Paras Lines Words Graphs Chars Bytes File
2 2270 82249 504169 504333 528663 /tmp/ap
1 2404 11163 63164 63164 66336 /tmp/b3
uwc: missing linebreak at end of corrupted textfiile /tmp/bad
1* 2* 4 19 19 19 /tmp/bad
1 14 52 273 273 293 /tmp/es
57 383 1369 11997 11997 12001 /tmp/funny
1 657068 3175429 31205970 31209138 32633834 /tmp/lw
1 1 4 27 27 27 /tmp/nf.cp1252
1 1 4 27 27 34 /tmp/nf.euc-jp
1 1 4 27 27 27 /tmp/nf.latin1
1 1 4 27 27 27 /tmp/nf.macroman
1 1 4 27 27 54 /tmp/nf.ucs2
1 1 4 27 27 56 /tmp/nf.utf16
1 1 4 27 27 54 /tmp/nf.utf16be
1 1 4 27 27 54 /tmp/nf.utf16le
1 1 4 27 27 112 /tmp/nf.utf32
1 1 4 27 27 108 /tmp/nf.utf32be
1 1 4 27 27 108 /tmp/nf.utf32le
1 1 4 27 27 39 /tmp/nf.utf7
1 1 4 27 27 31 /tmp/nf.utf8
1 26906 101528 635841 636026 661202 /tmp/o2
131 346 1370 9590 9590 4486 /tmp/perl5122delta.pod.gz
291 814 3941 25318 25318 9878 /tmp/perl51310delta.pod.bz2
1 2551 5345 132655 132655 133178 /tmp/tailsort-pl.utf8
1 89 334 1784 1784 2094 /tmp/til
1 4 18 88 88 106 /tmp/w
276 1736 5773 53782 53782 53804 /tmp/www
Здесь ya go:
#!/usr/bin/env perl
#########################################################################
# uniwc - improved version of wc that works correctly with Unicode
#
# Tom Christiansen <[email protected]>
# Mon Feb 28 15:59:01 MST 2011
#########################################################################
use 5.10.0;
use strict;
use warnings FATAL => "all";
use sigtrap qw[ die untrapped normal-signals ];
use Carp;
$SIG{__WARN__} = sub {
confess("FATALIZED WARNING: @_") unless $^S;
};
$SIG{__DIE__} = sub {
confess("UNCAUGHT EXCEPTION: @_") unless $^S;
};
$| = 1;
my $Errors = 0;
my $Headers = 0;
sub yuck($) {
my $errmsg = $_[0];
$errmsg =~ s/(?<=[^\n])\z/\n/;
print STDERR "$0: $errmsg";
}
process_input(\&countem);
sub countem {
my ($_, $file) = @_;
my (
@paras, @lines, @words,
$paracount, $linecount, $wordcount,
$grafcount, $charcount, $bytecount,
);
if ($charcount = length($_)) {
$wordcount = eval { @words = split m{ \p{Space}+ }x };
yuck "error splitting words: [email protected]" if [email protected];
$linecount = eval { @lines = split m{ \R }x };
yuck "error splitting lines: [email protected]" if [email protected];
$grafcount = 0;
$grafcount++ while /\X/g;
#$grafcount = eval { @lines = split m{ \R }x };
yuck "error splitting lines: [email protected]" if [email protected];
$paracount = eval { @paras = split m{ \R{2,} }x };
yuck "error splitting paras: [email protected]" if [email protected];
if ($linecount && !/\R\z/) {
yuck("missing linebreak at end of corrupted textfiile $file");
$linecount .= "*";
$paracount .= "*";
}
}
$bytecount = tell;
if (-e $file) {
$bytecount = -s $file;
if ($bytecount != -s $file) {
yuck "filesize of $file differs from bytecount\n";
$Errors++;
}
}
my $mask = "%8s " x 6 . "%s\n";
printf $mask => qw{ Paras Lines Words Graphs Chars Bytes File } unless $Headers++;
printf $mask => map( { show_undef($_) }
$paracount, $linecount,
$wordcount, $grafcount,
$charcount, $bytecount,
), $file;
}
sub show_undef {
my $value = shift;
return defined($value)
? $value
: "undef";
}
END {
close(STDOUT) || die "$0: can't close STDOUT: $!";
exit($Errors != 0);
}
sub process_input {
my $function = shift();
my $enc;
if (@ARGV == 0 && -t) {
warn "$0: reading from stdin, type ^D to end or ^C to kill.\n";
}
unshift(@ARGV, "-") if @ARGV == 0;
FILE:
for my $file (@ARGV) {
# don't let magic open make an output handle
next if -e $file && ! -f _;
my $quasi_filename = fix_extension($file);
$file = "standard input" if $file eq q(-);
$quasi_filename =~ s/^(?=\s*[>|])/< /;
no strict "refs";
my $fh = $file; # is *so* a lexical filehandle! ☺
unless (open($fh, $quasi_filename)) {
yuck("couldn't open $quasi_filename: $!");
next FILE;
}
set_encoding($fh, $file) || next FILE;
my $whole_file = eval {
use warnings "FATAL" => "all";
local $/;
scalar <$fh>;
};
if ([email protected]) {
[email protected] =~ s/ at \K.*? line \d+.*/$file line $./;
yuck([email protected]);
next FILE;
}
$function->($whole_file, $file);
unless (close $fh) {
yuck("couldn't close $quasi_filename at line $.: $!");
next FILE;
}
} # foreach file
}
sub set_encoding(*$) {
my ($handle, $path) = @_;
my $enc_name = "utf8";
if ($path && $path =~ m{ \. ([^\s.]+) \z }x) {
my $ext = $1;
die unless defined $ext;
require Encode;
if (my $enc_obj = Encode::find_encoding($ext)) {
my $name = $enc_obj->name || $ext;
$enc_name = "encoding($name)";
}
}
return 1 if eval {
use warnings FATAL => "all";
no strict "refs";
binmode($handle, ":$enc_name");
1;
};
for ([email protected]) {
s/ at .* line \d+\.//;
s/$/ for $path/;
}
yuck("set_encoding: [email protected]");
return undef;
}
sub fix_extension {
my $path = shift();
my %Compress = (
Z => "zcat",
z => "gzcat", # for uncompressing
gz => "gzcat",
bz => "bzcat",
bz2 => "bzcat",
bzip => "bzcat",
bzip2 => "bzcat",
lzma => "lzcat",
);
if ($path =~ m{ \. ( [^.\s] +) \z }x) {
if (my $prog = $Compress{$1}) {
return "$prog $path |";
}
}
return $path;
}
Ответ 5
Существует проект Perl Power Tools, целью которого является восстановление всех утилит bin для Unix, в первую очередь для тех, кто работает в операционных системах, лишенных Unix, Да, они сделали wc. Реализация переполнена, но это совместимый с POSIX.
Немного смешно, когда вы смотрите на совместимую с GNU реализацию true.
Ответ 6
Несерьезный ответ:
system("wc foo");
Ответ 7
Чтение файла в кусках фиксированного размера может быть более эффективным, чем чтение строк за строкой. Этот двоичный файл wc
делает это.
#!/usr/bin/env perl
use constant BLOCK_SIZE => 16384;
for my $file (@ARGV) {
open my $fh, '<', $file or do {
warn "couldn't open $file: $!\n";
continue;
};
my ($chars, $words, $lines) = (0, 0, 0);
my ($new_word, $new_line);
while ((my $size = sysread $fh, local $_, BLOCK_SIZE) > 0) {
$chars += $size;
$words += /\s+/g;
$words-- if $new_word && /\A\s/;
$lines += () = /\n/g;
$new_word = /\s\Z/;
$new_line = /\n\Z/;
}
$lines-- if $new_line;
print "\t$lines\t$words\t$chars\t$file\n";
}
Ответ 8
Чтобы иметь возможность считать CHARS, а не байты, рассмотрите следующее:
(Попробуйте с китайскими или кириллическими буквами и сохраните файл в utf8)
use utf8;
my $file='file.txt';
my $LAYER = ':encoding(UTF-8)';
open( my $fh, '<', $file )
|| die( "$file couldn't be opened: $!" );
binmode( $fh, $LAYER );
read $fh, my $txt, -s $file;
close $fh;
print length $txt,$/;
use bytes;
print length $txt,$/;
Ответ 9
Я наткнулся на это, в то время как googling для решения счетчика символов.
По общему признанию, я почти ничего не знаю о perl, поэтому некоторые из них могут быть недоступны, но вот мои хитрости решения для ньютов.
Во-первых, есть встроенная переменная счетчика строк, так что я просто использовал это. Наверное, это немного более эффективно.
Как бы то ни было, число символов включает символы новой строки, что, вероятно, не то, что вы хотите, поэтому я перепутал $_.
Perl также жаловался на то, как делается split() (неявное разделение, см. Почему Perl жалуется и "Использование неявного split to @_ устарело" ?) так что я подправил это.
Мои входные файлы - UTF-8, поэтому я открыл их как таковые. Вероятно, это помогает получить правильный счетчик символов во входном файле, содержащий символы, отличные от ASCII.
Здесь код:
open(FILE, "<:encoding(UTF-8)", "file.txt") or die "Could not open file: $!";
my ($lines, $words, $chars) = (0,0,0);
my @wordcounter;
while (<FILE>) {
chomp($_);
$chars += length($_);
@wordcounter = split(/\W+/, $_);
$words += @wordcounter;
}
$lines = $.;
close FILE;
print "\nlines=$lines, words=$words, chars=$chars\n";
Ответ 10
Это может быть полезно для начинающих Perl.
Я попытался имитировать функциональные возможности подсчета слов MS и добавил еще одну функцию, которая не показана с помощью wc в Linux.
- количество строк
- количество слов
- количество символов с пробелом
- количество символов без пробела (wc не даст этого в своем выходе, но слова Microsoft показывают это.)
Вот URL: Подсчет слов, символов и строк в файле