【问题标题】:Why is my perl split(), at all 3rd occurences of a space not working?为什么我的 perl split() 在所有第 3 次出现空格时都不起作用?
【发布时间】:2021-10-04 13:22:24
【问题描述】:

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

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=

【问题讨论】:

  • perl -0777 -pe's/\s+/(++$n % 3)?" ":"\n"/eg' input.txt &gt; output.txt

标签: perl


【解决方案1】:

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

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

die "Usage: $0 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) 未使用local 定义,然后&lt;&gt; operator 读取名称为@ARGV 的整个文件,所以(第一个)在命令行上给出。这是分配给$content,我们得到了我们的文件。

然后split 使用其惯用的' ' 模式将该字符串分隔为任何空格,从而为我们提供文件中的单词列表。

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

所有这些都可以放在一个陈述中,但这里没有理由做这种杂技。

更重要的是,这可以通过使用库来完成。我们可以使用Path::Tiny::slurp 将文件读入一个变量,并使用List::MoreUtils::natatime (n-at-a-time) 处理元素组。


\ndie 语句末尾的... at program-name line 5. 抑制了通常打印的... at program-name line 5.,因为它在这里没有多大意义。这是非常基本的,并且有更好的方法来提供和处理用户输入。见Getopt::Long

另见this section in perlvar

【讨论】:

  • 谢谢@zdim。看起来你简洁的代码可以做我想要的。你能指导我吗,我想从文件中读取数据。我不明白我怎么能做到这一点。此外,这最终必须每次都使用不同的内容实现自动化,因此在命令行上运行它不是我的最佳选择。在命令行上,我猜它的运行方式是“$0 perlscript inputfile”。请纠正我。
  • 好吧,你以perl scriptname.pl input-filename 运行它。如果您使脚本可执行,那么它就是scriptname.pl input-filename,就像它说的那样。 (第一行,die...,是使用信息。)然后它以同样的方式运行在程序之外,就像任何命令运行在程序之外一样。可以使用system,或qx(反引号),或者——更好的——库,比如IPC::System::SimpleCapture::TinyIPC::Run。这是你要问的吗?
  • 因此,在一个应该运行它的程序中,您将拥有system(scriptname.pl $filename)...(或使用qx,或库),无论以何种方式,该程序都会获得$filename。你知道如何从程序中运行外部命令吗?您以完全相同的方式运行此脚本。
  • 我将此信息添加到代码下方的答案中。请记住,该文件必须是可执行的(在 linux 中为 chmod u+x scriptname),或者为此添加 perl ...
  • 是的,你很清楚。在代码中,带有“die ...”的行对于输入数据文件的来源不是很清楚。我非常熟悉在 Perl 中运行系统命令。我已经运行了代码。它完美地工作。我非常感谢您的时间和精力。非常感谢。
【解决方案2】:

请调查以下代码 sn-p 是否符合您的问题。

注意:要从文件中读取数据,请将 &lt;DATA&gt; 替换为 &lt;&gt;

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=

【讨论】:

    【解决方案3】:

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

    my @alldata=<$TMP>;
    

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

    那你join每一行用空格变成一个字符串。

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

    但不会删除换行符。例如,如果您有数组。

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

    然后用空格连接这个数组,就得到了字符串。

    "foo\n bar\n baz\n"
    

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

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

    最重要的是,当您使用 split 时只有一个空格,这是一种特殊情况。然后将其视为split /\s+/。这意味着它不仅会拆分空格字符,还会拆分所有空格字符,并且同时拆分多个空格字符。

    此外,有时最好使用正则表达式来删除这样的换行符。

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

    【讨论】:

      猜你喜欢
      • 2020-03-04
      • 2021-06-18
      • 1970-01-01
      • 2012-01-31
      • 1970-01-01
      • 1970-01-01
      • 2010-10-18
      • 2014-05-24
      • 1970-01-01
      相关资源
      最近更新 更多