假设您有一个文件,其中包含五个数据块,由两个或多个\n
分隔,以分隔记录(通用文本格式)。
如果您使用RS=""
运行awk,则设置awk以将块分隔为记录。然后,您可以设置FS=\n
以将块的行分隔为字段。
示例:
$ cat lines
f1, r1
f2, r1 then 2 \n:
f1, r2 then 3 \n:
f1,r3
f2,r3 then 4 \n:
f1, r4
f2,r4 then 6 \n:
f1,r5
使用awk将块分隔成记录并将行分隔成字段的idiomatic方法是:
$ awk 'BEGIN{RS=""; FS="\n"; OFS="|"}
{$1=$1; printf "NR: %d, NF: %d, record: \"%s\"\n", NR, NF, $0 }' lines
NR: 1, NF: 2, record: "f1, r1|f2, r1 then 2 \n:"
NR: 2, NF: 1, record: "f1, r2 then 3 \n:"
NR: 3, NF: 2, record: "f1,r3|f2,r3 then 4 \n:"
NR: 4, NF: 2, record: "f1, r4|f2,r4 then 6 \n: "
NR: 5, NF: 1, record: "f1,r5"
无论\n
分隔多少块,只要2个或更多,它就是一条记录。
(gawk可以通过设置RS="\n\n+"
代替RS=""
来获得完全相同的结果,因为gawk支持正则表达式来分隔记录。感谢Ed Morton指出了POSIX awk和gawk之间的差异)
虽然perl不支持将正则表达式用于输入记录分隔符,但有两种方法可以设置等效的段落模式。您可以使用-00
命令行开关或将输入记录分隔符$/
设置为空字符串:
$ perl -00 -F"\n" -lane 'BEGIN{ $\=""; $,="|"}
printf "NR: %d, NF: %d, record: \"%s\"\n", $., scalar(@F), join($,,@F)' lines
NR: 1, NF: 2, record: "f1, r1|f2, r1 then 2 \n:"
NR: 2, NF: 1, record: "f1, r2 then 3 \n:"
NR: 3, NF: 2, record: "f1,r3|f2,r3 then 4 \n:"
NR: 4, NF: 2, record: "f1, r4|f2,r4 then 6 \n: "
NR: 5, NF: 1, record: "f1,r5"
或者,
$ perl -F"\n" -lane 'BEGIN{ $\=""; $,="|"; $/=""}
printf "NR: %d, NF: %d, record: \"%s\"\n", $., scalar(@F), join($,,@F)' lines
也有效 - 相同的输出。
Ruby 有一个段落模式,但与Perl和awk不同,它有一个可能很重要的行为差异。如果有\n
个以上,则\n
的运行不会被忽略。它等同于Ruby中的正则表达式/\n\n/
与awk和Perl中的/\n\n+/
。它会将相同输入的字段计数和记录计数搞砸。
演示:
$ ruby -00 -F"\n" -lane 'BEGIN{$\=""; $,="|"};
printf "NR: %d, NF: %d, record: \"%s\"\n", $.,$F.length,$F.join' lines
NR: 1, NF: 2, record: "f1, r1|f2, r1 then 2 \n:"
NR: 2, NF: 1, record: "f1, r2 then 3 \n:"
NR: 3, NF: 3, record: "|f1,r3|f2,r3 then 4 \n:"
NR: 4, NF: 0, record: ""
NR: 5, NF: 2, record: "f1, r4|f2,r4 then 6 \n: "
NR: 6, NF: 0, record: ""
NR: 7, NF: 0, record: ""
NR: 8, NF: 1, record: "f1,r5"
因此,当Perl和Awk认为它有5个记录和8个总字段时,Ruby的-00
段模式认为相同的内容有8个记录,总共9个字段。
有没有办法用Ruby获得与Perl和Awk相同的结果?
答案 0 :(得分:2)
如果您使用$/=""
代替-00
:
$ ruby -F"\n" -lane 'BEGIN{$/=""; $\=""; $,="|"; $i=1};
print "#{$F.join($,)}\t\t#{$i}\n"; $i+=1;' lines
这相当于Perl命令:
$ perl -F"\n" -lane 'BEGIN{$/=""; $\=""; $,="|"; $i=1}
print join($,,@F)."\t\t$i\n"; $i++;' lines
两个输出:
f1, r1|f2, r1 then 2 \n: 1
f1, r2 then 3 \n: 2
f1,r3|f2,r3 then 4 \n: 3
f1, r4|f2,r4 then 6 \n: 4
f1,r5 5
答案 1 :(得分:0)
与Perl一样,Ruby仅支持$/
的单个八进制字符来分隔记录。 (Ruby和Perl共享类似的全局变量。)
所以这些是三种解决方法:
设置$/=""
。在Ruby中,$/=""
的行为与Perl相同,其中\n
的运行被视为单个记录分隔符(与ruby -00
形成对比)。 (感谢Stefan)
'啜食'该文件然后使用正则表达式将文本分成记录和字段。 (对于perl,POSIX awk或ruby中非单个八进制字符或\n\n+
的记录之间的任何中断,您需要执行此操作。)
通过awk提取文件以删除多余的\n
并将中断重新定义为\n\n
。
$ ruby -F"\n" -lane 'BEGIN{$\=""; $/=""; $,="|"};
printf "NR: %d, NF: %d, record: \"%s\"\n", $.,$F.length,$F.join' lines
NR: 1, NF: 2, record: "f1, r1|f2, r1 then 2 \n:"
NR: 2, NF: 1, record: "f1, r2 then 3 \n:"
NR: 3, NF: 2, record: "f1,r3|f2,r3 then 4 \n:"
NR: 4, NF: 2, record: "f1, r4|f2,r4 then 6 \n: "
NR: 5, NF: 1, record: "f1,r5"
$ ruby -e 'i=0
$<.read.split(/\n\n+/)
.map {|record| record.split(/\n/)}
.map {|f| i+=1; printf "NR: %d, NF: %d, record: \"%s\"\n", i,f.length,f.join
}' lines
$ ruby -00 -F"\n" -lane 'BEGIN{$/=""; $\=""; $,="|"; $i=1};
printf "NR: %d, NF: %d, record: \"%s\"\n", $.,$F.length,$F.join' <(awk 'BEGIN{RS=""} {print $0 ORS}' lines)
所有产生与第一个产生相同的输出。
ruby -00
的行为不与perl等价物相同;它等同于打破正则表达式/\n\n/
如果您知道数据块仅由两个-00
分隔,则仅使用\n
。
(至少在-00
ruby -0
上修复...)
(注意:ruby -0[some octal value]
与0x00
不同前者将输入记录分隔符设置为find . print0 | ruby -0 -lane 'puts $_'
的文字值,以便与可以提供Nul终止字符串的其他Unix实用程序一起使用,例如/images/image_id=abc123
)