问题描述
|
我有一个连续的大文本,上面有一些字符,例如characters0ѭ,中间有空格。我想阅读此文本,然后在找到这些字符的任何地方写到新行中。
输入文字如下:
apple{{mango } guava ; banana; // pear Berry;}
预期的格式化输出数据应如图所示
apple
{
{
mango
}
guava ;
banana;
// pear
Berry;
}
我想在perl中做到这一点。
解决方法
当然,您必须根据需要调整它(最明显的是在读取行时循环),但这是一种不(真的)依赖正则表达式的方法。正如其他人所说,这是一个起点,您可以适应所需的内容。
#!/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\";
这会将行分割成一个数组,分割成非单词字符,但保留了元素。然后摸索非空格字符(删除包含空格的数组元素)。然后将带有换行符的数组元素连接到一个字符串中。根据您的说明,您一共需要4英镑,我作为练习留给读者。
编辑:
再次查看您的请求之后,您似乎想要尝试解析一个特定但复杂的结构。要正确执行此操作,您可能必须使用功能更强大的产品,例如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的评论,我修改了上面的示例以允许在同一行上包含多个单词,到目前为止,这些单词只能用一个空格分隔。我还使注释与以match4开头并以两个或多个空格结尾的任何内容匹配。另外,由于我进行了更改,并且由于我相信这是代码漂亮的打印机,因此我在制表符格式化程序中添加了制表符。如果不希望这样做,则应该很容易剥离。现在去学习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;}
, 您的规格糟透了。有时您需要换行符前后。有时您需要换行符。有时您之前需要换行符。您在单独的行上有\“ pear \”和\“ berry \”,但是它不符合规范中的任何条件。
答案的质量与撰写问题时的注意程度成正比。
有了一个粗心的问题,您可能会得到一个粗心的答案。
#!/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;
, 不完全是您想要的,但是恕我直言就足够了:
echo \'apple{{mango } guava ; banana; // pear berry;}\' |\\
perl -ple \'s/(\\b\\w+\\b)/\\n$1\\n/g\'
将产生:
apple
{{
mango
}
guava
;
banana
; //
pear
berry
;}
您可以开始对其进行改进...
, 正如您所说的,这不是功课,我想到以下内容:
我的$ keeps = qr#(// \\ s + \\ w +)#; #special tokens to keep(e.g.,perl)
我的$ breaks = qr#(\\ s + | \\ [| \\] | \\ {| \\})#; #simple令牌可在处拆分单词
同时(my $ text = <>)
{
@tokens = grep / \\ S /,split(qr($ keeps | $ breaks),$ text);
打印join(\“。\\ n。\”,@tokens),\“ \\ n \”;
}
您将必须自己制定实际规则。