在Perl脚本中切换行的长度

时间:2017-05-10 21:16:52

标签: perl

我想在Perl中使用switch / case构造。 我有一个包含一系列单词的文件,我想根据该行所包含的单词数对每行进行不同的处理。

示例文件:

w1 w2 w2
w1 w3

因此脚本看起来像这样,但我如何计算每行中的单词数?

given ($number_of_word_in_line) {
   when ($_ > 2) {
       ...
   }
   when ($_ > 3) {
       ...
   }
   default {
       ...
   }
}

2 个答案:

答案 0 :(得分:4)

请注意switch statement

highly experimental
  

如前所述,“切换”功能被认为是高度实验性的;它几乎没有变化通知。特别是,when具有棘手的行为,预计将在未来变得不那么棘手。不要依赖其当前(错误)实施。在Perl 5.18之前,given还有一些棘手的行为,如果你的代码必须在旧版本的Perl上运行,你仍应该注意这些行为。

这些 非常棘手,更改。

话虽如此,计算字符串中单词的一种方法是首先split

use warnings;
use strict;
use feature 'switch';

my $file = '...';
open my $fh, '<', $file  or die "Can't open $file: $!";

while (my $line = <$fh>)
{
    chomp $line;
    my @words = split ' ', $line;
    my $num_words = @words;

    given ($num_words) {
        when ($num_words > 2) { 
            # ...
        }
    }
}
close $fh;

什么使用标量($num_words)在分配数组(@words)时接收数组元素的数量。见Context in perldata

  

赋值有点特别之处在于它使用其左参数来确定右参数的上下文。对标量的赋值评估标量上下文中的右侧,[...]

并且在标量上下文中计算的数组会产生其元素的数量。

为了在不创建数组变量的情况下获取计数,我们需要为标量分配一个列表,这是不可能直接进行的。但是有一些解决方法,例如

my $num_words = () = $line =~ /\w+/g;

其中"operator" = () =是关于上下文的游戏,或

my $num_words = @{ [ $line =~ /\w+/g ] };

其中[]引用内部列表,然后由@{ }取消引用,可以分配给标量。此方法也适用于split ' ', $line

有关列表,数组和标量的大量信息,请参阅this page

这可以更紧凑地编写为

while (<$fh>) {
    chomp;
    my $num_words = @{ [ split ] };
    # ...
}

whilechompsplit的默认值为$_ variablesplit也需要一种模式,默认值为' ',因此上述内容与split ' ', $_相同。模式' '对于split是特殊的,并匹配任何数量的任何空格,也会丢弃前导和尾随空格。

答案 1 :(得分:2)

计算一行中的字数是许多可能的解决方案的问题。这是一个非常简单的问题:

sub count_words {
    my($line) = @_;

    my @words = split ' ', $line;
    return scalar(@words);
}

my $line = " The  quick brown fox jumps over the  lazy dog \n";

say "count_words(): " . count_words($line);  # prints '9'

通常Perl的split函数将第一个参数视为正则表达式,但如果参数是一个只包含一个空格的字符串,则丢弃前导空格,并使用正则表达式/\s+/。这允许跳过多个连续的空格字符,并且还会丢弃尾随空格。

你没有提到你想要算什么类型的'单词'。是书面语言吗?会有标点符号吗?是ASCII文本吗?根据这些问题的答案,使用正则表达式“捕获”单词可能会获得更好的结果:

sub count_words {
    my($line) = @_;

    my @words = $line =~ /(\w+)/g;
    return scalar(@words);
}

这将处理标点符号周围缺少的空格(例如:“一,二,三”将被视为三个单词,而分裂则将其视为一个单词)。但它不适用于撇号(例如:“不会”将被视为两个单词)并且它不适用于非ASCII字符(例如:“réfrigérateur”将被视为三个单词)。

要在构成单词的字符列表中包含撇号,可以将正则表达式行更改为:

    my @words = $line =~ /([\w']+)/g;

但是,如果您的文本已将ASCI撇号更改为“智能引号”字符,那么您可能需要以下内容:

    my @words = $line =~ /([\w'\x{2019}]+)/g;

要允许正则表达式的\w部分与重音字符匹配,可以在脚本顶部添加:

use utf8;

无论像é这样的字符是作为单个代码点U+00E9表示,还是作为两个带有普通字母和组合字符重音的代码点,这似乎都有效:U+0065 {{ 3}}

另一位用户对您的问题的评论表明他们认为您可能想要计算一行中的唯一单词(例如:“一加一”将被视为两个独特的单词)。如果是这样,您将需要使用哈希将@words缩减为唯一列表。