Perl:在找到特定字符时将文本写入新行

时间:2011-06-11 11:05:11

标签: perl

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

输入文字如下:

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

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

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

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

4 个答案:

答案 0 :(得分:4)

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

#!/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的评论,我已经调整了上面的例子以允许在同一行上有多个单词,现在这些单词只能用一个空格分隔。我还发表评论,匹配从//开始并以两个或多个空格结束的任何内容。此外,由于我正在进行更改,并且由于我认为这是一个代码漂亮的打印机,我添加了tabbing到块格式化程序。如果不希望这样,它应该很容易剥离。现在就去学习Regexp::Grammars并使其适合您的具体情况。 (我知道我应该让OP做这个改变,但我也很乐意学习它)

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

#!/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;}

答案 1 :(得分:3)

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

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

如果有一个粗心的问题,你可能会得到一个粗心的答案。

#!/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;

答案 2 :(得分:1)

不完全是你想要的,但是imho的开始就足够了:

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

将产生:

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

berry
;}

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

答案 3 :(得分:1)

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

my $keeps  = qr#(//\s+\w+)#;            #special tokens to keep  (e.g., // perl)
my $breaks = qr#(\s+|\[|\]|\{|\})#;     #simple tokens to split words at

while ( my $text = <> )
{
    @tokens = grep /\S/, split( qr($keeps|$breaks), $text );
    print join(".\n.", @tokens ), "\n";
}

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