【问题标题】:Perl: Writing text into new line when a particular character is foundPerl:找到特定字符时将文本写入新行
【发布时间】:2011-06-11 11:05:10
【问题描述】:

我有一个大的连续文本,其中包含{, },//,; 之类的字符和中间的空格。我想阅读此文本并在找到这些字符的任何地方写入新行。

输入文字如下:

apple{{mango } guava ; banana; // pear      berry;}

预期的格式化输出数据应如图所示

apple
{
{
mango
}
guava ;
banana;
// pear
berry;
}

我想在 perl 中执行此操作。提前致谢。

【问题讨论】:

    标签: perl


    【解决方案1】:

    当然,您必须根据自己的需要进行调整(最明显的是在读取行时循环),但这是一种不(真正)依赖正则表达式的方法。正如其他人所说,这是一个起点,您可以根据需要进行调整。

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    my $string = 'apple{{mango } guava ; banana; // pear      berry;}';
    my $new_string = join("\n", grep {/\S/} split(/(\W)/, $string));
    
    print $new_string . "\n";
    

    这会将行拆分为一个数组,拆分非单词字符但保留元素。然后 greps 查找非空白字符(删除包含空白的数组元素)。然后将带有换行符的数组元素连接成一个字符串。根据您的规范所说,您需要 // 一起,我将其作为练习留给读者。

    编辑: 再次查看您的请求后,您似乎想要解析一个特定但复杂的结构。要正确执行此操作,您可能必须使用更强大的东西,例如 Regexp::Grammars 模块。这需要一些学习,但您可以定义一组非常复杂的解析指令来完成您需要的任何操作。

    编辑 2: 由于我一直在寻找更多了解Regexp::Grammars 的理由,因此我抓住了这个机会。这是我想出的一个基本示例。它将解析的数据结构打印到名为“log.txt”的文件中。我知道它看起来不像您要求的结构,但它包含所有这些信息,并且可以根据您的喜好进行重组。我使用了一个与解析器基本相反的递归函数。

    #!/usr/bin/env perl
    use strict;
    use warnings;
    
    use Data::Dumper;
    use Regexp::Grammars;
    
    my $grammar = qr{
      <nocontext:>
      <Line>
      <rule: Line>      <[Element]>*
      <rule: Element>   <Words> | <Block> | <Command> | <Comment>
      <rule: Command>   <[Words]> ;
      <rule: Block>     \{ <[Element]>* \}
      <rule: Comment>   // .*? \s{2,}        #/ Syntax Highlighter fix
      <rule: Words>     (?:\b\w+\b) ** \s
    }x;
    
    my $string = 'apple{{mango kiwi } guava ; banana; // pear      berry;}';
    
    if ($string =~ $grammar) {
      open my $log, ">", "log.txt";
      print $log Dumper \%/; #/
    
      print elements($/{Line}{Element});
    
    } else {
      die "Did not match";
    }
    
    sub elements {
      my @elements = @{ shift() };
      my $indent = shift || 0;
      my $output;
    
      foreach my $element (@elements) {
        $output .= "\t" x $indent;
    
        foreach my $key (keys %$element) {
          if ($key eq 'Words') {
            $output .= $element->{$key} . "\n";
          } elsif ($key eq 'Block') {
            $output .= "{\n" . elements($element->{$key}->{Element}, $indent + 1) . ("\t" x $indent) . "}\n";
          } elsif ($key eq 'Comment') {
            $output .= $element->{$key} . "\n";
          } elsif ($key eq 'Command') {
            $output .= join(" ", @{ $element->{$key}->{Words} }) . ";\n";
          } elsif ($key eq 'Element') {
            $output .= elements($element->{$key}, $indent + 1);
          }
        }
      }
    
      return $output;
    }
    

    编辑 3:根据 OP 中的 cmets,我已经修改了上面的示例以允许在同一行上显示多个单词,截至目前,这些单词只能用一个空格分隔。我还让 cmets 匹配以 // 开头并以两个或多个空格结尾的任何内容。此外,由于我正在进行更改,并且因为我相信这是一个代码漂亮的打印机,所以我在块格式化程序中添加了制表符。如果不希望这样做,它应该很容易剥离。现在去学习Regexp::Grammars,让它适合你的具体情况。 (我知道我应该让 OP 做这个改变,但我也很喜欢学习它)

    编辑 4:还有一件事,如果实际上您正在尝试将有用的代码从序列化代码恢复为单行代码,那么您唯一真正的问题是提取行 cmets 并将它们与有用的代码分开(假设您使用的是空格忽略看起来好像你的语言)。如果是这种情况,那么也许可以在我的原始代码上尝试这种变体:

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    my $string = 'apple{{mango } guava ; banana; // pear      berry;}';
    my $new_string = join("\n", split(/((?:\/\/).*?\s{2,})/, $string));
    
    print $new_string . "\n";
    

    谁的输出是

    apple{{mango } guava ; banana; 
    // pear      
    berry;}
    

    【讨论】:

    • 它可以工作,只是它在每个单词处换行......不仅仅是特殊字符......任何建议......
    • @Sumit,我认为你正在做的是解析更复杂的东西。您将不得不调整规则以适应您拥有的结构。甚至您的示例也不仅限于特殊字符;注释机制// words (multiple spaces) 是一行。该框架将允许您声明您打算在逻辑上匹配的不同类型的结构。如果我想太多了,那么我很抱歉,但是,我猜你正在打印一些编程语言,你需要做得比在特殊字符上拆分更好。
    • @Sumit,您可以添加类似&lt;rule: Words&gt; &lt;[Word]&gt;+ 的规则并将元素规则设为&lt;rule: Element&gt; &lt;Words&gt; | ...。这会将多个单词放在一个块中。您还必须修改递归函数来处理这个问题。注意:这未经测试,在我看来,可能会破坏命令规则,您可能需要将其更改为 &lt;Words&gt; ; 并可能需要进行其他一些清理。您应该能够从这里自己调整它。我承认这并不容易,但它功能强大、灵活且合乎逻辑。一旦你做对了,它就会很好地为你服务。
    • @Sumit,我更新了示例,因为我对解决方案感兴趣,从现在开始,使用/调整它以满足您的需求。
    • @joel ..thx 在那里纠正了我......是的,它并没有中断 //......我正在尝试学习 regexp:grammar....
    【解决方案2】:

    你的规格很烂。有时您需要在前后换行。有时你想要一个换行符。有时您之前需要换行符。您在不同的行上有“梨”和“浆果”,但它不符合规范中的任何条件。

    答案的质量与撰写问题时给予的关注度成正比。

    一个粗心的问题,你很可能得到一个粗心的答案。

    #!/usr/bin/perl
    use warnings;
    use strict;
    
    $_ = 'apple{{mango } guava ; banana; // pear      berry;}';
    
    s#([{}])#\n$1\n#g; # curlies
    s#;#;\n#g;         # semicolons
    s#//#\n//#g;       # double slashes
    s#\s\s+#\n#g;      # 2 or more whitespace
    s#\n\n#\n#g;       # no blank lines
    
    print;
    

    【讨论】:

    • 完全同意你的看法。他的问题不够清晰。
    • pear 和 berry 之间有很长的空白空间......我是 perl 新手......我想这种问题对你来说可能听起来很基础......无论如何我感谢你的帮助。 .
    • @Sumit:基本问题很好。但经过深思熟虑的却不是。您需要以清晰简洁的方式定义您的规范,然后才能适应代码。有条不紊、清晰地表达自己并没有什么进步。
    • @DavidO 这个文本将是大行,而不是像芒果或苹果这样的世界......它就像阅读一个大的单行文本。逐个字符检查此类; { } 。当它发生时将读取的文本打印到新行并在新行中打印这个特殊字符。继续阅读该行,直到再次发生这种情况...
    【解决方案3】:

    不完全是你想要的,但恕我直言,一开始就足够了:

    echo 'apple{{mango } guava ; banana; // pear      berry;}' |\
    perl -ple 's/(\b\w+\b)/\n$1\n/g'
    

    将产生:

    apple
    {{
    mango
     } 
    guava
     ; 
    banana
    ; // 
    pear
    
    berry
    ;}
    

    你可以开始改进它了...

    【讨论】:

    • @ jm666 Thx 这将起作用...... -ple 's/(\b\w+\b)/\n$1\n/g' 这个工作原理......
    • @jm666 ...这与实际文件中的文本不完全相同....在大文件中,我必须阅读文本,直到字符像; { } 出现......然后将直到这一点的文本打印到新行......再次继续阅读,直到下一次再次发生并继续循环
    • @jm666 此文本将是大行,而不仅仅是像芒果或苹果这样的世界......它就像阅读一大行文本。逐个字符检查此类; { } 。当它发生时将读取的文本打印到新行并在新行中打印这个特殊字符。继续阅读该行,直到再次发生这种情况......
    • 简单告诉什么字符应该作为“行分隔符”,所以在你想要什么字符之前换行。
    • @jm666 从逻辑上讲,你所说的听起来正是我想要的......你能建议......我尝试类似:for $line () { while ($line =~ /( .)/g) { $line =~ s/;/\n/ ; $line =~ s/}/\n/ ;每次找到这样的字符但错过了这个字符时,它都会将文本换成新行......
    【解决方案4】:

    正如你所说,这不是家庭作业,我想到了以下内容:

    我的 $keeps = qr#(//\s+\w+)#; #要保留的特殊标记(例如,// perl)
    我的 $breaks = qr#(\s+|\[|\]|\{|\})#; #用于分割单词的简单标记
    
    而(我的 $text =  )
    {
        @tokens = grep /\S/, split( qr($keeps|$breaks), $text );
        print join(".\n.", @tokens ), "\n";
    }

    您必须自己制定实际规则。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-29
      • 2018-05-22
      • 1970-01-01
      相关资源
      最近更新 更多