为什么我的 perl split() 在 space 的所有第 3 次出现时都不起作用?

Why is my perl split(), at all 3rd occurences of a space not working?

我想重写下面的文字,将其分成一行中的三个字符串。我从一个文件中读取文本作为一个数组。接下来,我将数组的所有元素(4 行)连接到一个标量变量中。然后,我尝试在每出现三次 space 时拆分连接的元素。 我的代码如下。

open ($TMP, "< TTaaInLittler.txt") or die "open 'TTaaInLittler.txt: failed $! ($^E)";
my @alldata=<$TMP>;
my $oneline=join(" ", @alldata);
close$TMP;

my $i = 0;
my $n = 3;

my @oneline=split(" ", $oneline, 10) if !( ++$i % $n );
print @oneline;

join() 命令似乎有效,因为“print $oneline”打印了所有文本。打印 $oneline,但是会打印 4 行。我期待一条线。 split 命令似乎不起作用,因为“print @oneline”没有做任何事情;没有错误,没有输出。 有出路吗?请帮忙。

TTAA 58231 63741 99823 15423 17003 70152 07604 29517 50586 04381
08513 40758 16182 11524 30967 31964 00510 25094 41365 25503
20241 53562 10512 15419 68542 07540 10656 76156 11024 88123
76950 09548 77999 31313 47708 82318=
 
TTAA 58231 63741
99823 15423 17003
70152 07604 29517
50586 04381 08513
40758 16182 11524
30967 31964 00510
25094 41365 25503
20241 53562 10512
15419 68542 07540 
10656 76156 11024
88123 76950 09548
77999 31313 47708 
82318=

一种方法是将所有输入分解为单词,然后在一行上每三个单词打印一次

use warnings;
use strict; 
use feature 'say';

die "Usage: [=10=] filename\n" if not @ARGV;

# "Slurp" into a variable the file with the name given on the command line
my $content = do { local $/; <> };

my @words = split ' ', $content;

say join(' ', splice @words, 0, 3)  while @words;

当 运行 为 scriptname.pl input-filename.

时,这会根据给定输入文件的需要进行打印

下面是简要说明。

命令行中给出的参数在 Perl 程序中 @ARGV。所以我们首先测试 @ARGV 是否可能为空;如果用户确实没有提供所需的参数(在此处输入文件名),则程序无能为力,我们 die(退出),并带有简洁的用法消息。

文件被立即读入 do 块中的字符串。 “输入记录分隔符”($/ variable) is undefined using local and then the <> operator 读取名称在@ARGV 中的整个文件,因此(第一个)在命令行上给出。这是分配给 $content 我们得到了文件。

然后 split 使用其惯用的 ' ' 模式用任何白色 space 打断该字符串,为我们提供文件中的单词列表。

最后,splice 删除并 returns 前三个单词,它们由 space 连接并打印在一行上,只要 returns 中有任何内容,就会继续这样做=20=]。 (如果它的目标数组中没有三个元素,它会删除尽可能多的元素,最后耗尽并清空数组。)

所有这些都可以用一句话来概括,但是没有理由在这里进行这种杂技表演。

更重要的是,这可以通过使用库来完成。我们可以使用 Path::Tiny::slurp, and process groups of elements using List::MoreUtils::natatime(一次 n 个)将文件读入一个变量。


最后的\ndie statement suppresses the ... at program-name line 5., which is normally printed, since it wouldn't mean much here. This is very rudimentary, and there are much better ways to provide for and handle user input. See Getopt::Long

另见 this section in perlvar

请调查以下代码片段是否符合您的问题。

注意:要从文件中读取数据,请将 <DATA> 替换为 <>

use strict;
use warnings;

my($num,$count) = (3,1);
my @array = split(' ', do { local $/; <DATA> });

print $_, ($count++ % $num) ? ' ': "\n" for @array;

__DATA__
TTAA 58231 63741 99823 15423 17003 70152 07604 29517 50586 04381
08513 40758 16182 11524 30967 31964 00510 25094 41365 25503
20241 53562 10512 15419 68542 07540 10656 76156 11024 88123
76950 09548 77999 31313 47708 82318=

输出

TTAA 58231 63741
99823 15423 17003
70152 07604 29517
50586 04381 08513
40758 16182 11524
30967 31964 00510
25094 41365 25503
20241 53562 10512
15419 68542 07540
10656 76156 11024
88123 76950 09548
77999 31313 47708
82318=

您的代码和 split 工作正常。要了解您的问题,您应该了解您的代码实际做了什么。首先,如果你这样做。

my @alldata=<$TMP>;

然后它读取数组中的整个文件,但每一行最后仍然包含一个换行符。

然后你 join 每一行 space 变成一个字符串。

my $oneline=join(" ", @alldata);

但是没有删除换行符。例如,如果您有数组。

["foo\n", "bar\n", "baz\n"]

然后用space连接这个数组,你得到字符串。

"foo\n bar\n baz\n"

如果你打印这个字符串,当然,它会打印多行。因为它仍然包含多行。您可以通过在加入数组之前压缩数组来避免此问题。

chomp @data;
my $str  = join " ", @data;

最重要的是,当您将 split 与 space 一起使用时,有一种特殊情况。然后它被视为split /\s+/。这意味着它不仅在 space 个字符上拆分,而且在所有白色 space 字符上拆分,并且一次拆分多个字符。

此外,有时像这样使用正则表达式删除换行符会更好。

s/\r?\n// for @data;

默认情况下,chomp 会删除特定于您的操作系统的换行符。所以在 Linux 上它只会删除 \n 在 Windows 上它会删除 \r\n。但是如果你处理跨平台文件。最好用正则表达式删除换行符,因为它总是处理这两种情况。

加入每一行后,你可以再次拆分它。

因此您的代码可能如下所示:

# Remove newline at end of every line
s/\r?\n// for @data;

# join every line with whitespace
my $str = join " ", @data;

# split into tokens
my @tokens = split /\s+/, $str;

例如,要在一行上打印 10 个项目,您可以使用类似这样的方法。完整示例:

my @data = <DATA>;

# Remove newline at end of every line
s/\r?\n// for @data;

# join every line with whitespace
my $str = join " ", @data;

# split into tokens
my @tokens = split /\s+/, $str;

# print 10 tokens on every line
my $counter = 0;
for my $token (@tokens) {
    print $token, " ";
    if ( ++$counter == 10 ) {
        print "\n";
        $counter = 0;
    }
}


__DATA__
TTAA 58231 63741
99823 15423 17003
70152 07604 29517
50586 04381 08513
40758 16182 11524
30967 31964 00510
25094 41365 25503
20241 53562 10512
15419 68542 07540
10656 76156 11024
88123 76950 09548
77999 31313 47708
82318=

但以我的愚见,如果你想用 whitesapce 拆分所有内容,而忽略行,你可以做得更好,只需拆分每一行,然后将结果推送到数组中。这样做吧。

my @tokens;
for my $line (<DATA>) {
    push @tokens, split(/\s+/, $line);
}

我应该这样改变它的一个原因是,它更容易工作。而且您也不需要阅读整个文件,例如,如果您愿意,您也可以轻松地将数字插入 @tokens

my @tokens;

for my $line (<DATA>) {
    # Extracts only numbers from a line
    my @vals = $line =~ m/\d+/g;
    push @tokens, @vals;
}

你甚至可以进一步减少一切只是为了。

my @tokens = map { m/\d+/g } <DATA>;

map 为每个数组元素执行代码块。在这种情况下,对于 <DATA> 中的每一行,并将当前行放入特殊变量 $_ 中。一个正则表达式,默认匹配 $_m/\d+/g 匹配 $_ 并将其拆分为多个元素,每一行的所有元素都进入 @tokens.