答案1
用于在输出中hd
显示 FS 字符(十六进制):1c
$ perl -0777 -pe 's/^(\d{3}.*)\n/$1/mg' input.txt | hd
00000000 30 30 31 1c 42 61 6b 65 72 20 53 74 2e 4c 6f 6e |001.Baker St.Lon|
00000010 64 6f 6e 1c 33 1c 34 1c 37 0a 30 30 32 1c 50 65 |don.3.4.7.002.Pe|
00000020 6e 6e 79 20 4c 61 6e 65 4c 69 76 65 72 70 6f 6f |nny LaneLiverpoo|
00000030 6c 1c 38 38 1c 35 1c 37 0a |l.88.5.7.|
00000039
如果没有hd
,输出看起来像这样( FS 字符不可见但仍然存在,因此它们将要-i
如果它被重定向到另一个文件,或者如果使用“就地编辑”选项,则位于输出中):
$ perl -0777 -pe 's/^(\d{3}.*)\n/$1/mg' input.txt
001Baker St.London347
002Penny LaneLiverpool8857
在这两种情况下,此 Perl 脚本会立即读取整个文件 ( -0777
),并捕获每个“行”(以三位数字开头的字符序列,直到但不包括下一个换行符)并将其替换为捕获的文本 (没有换行符)。简而言之,它从任何以三位数字开头的“行”中删除换行符。
$1
如果您希望用空格替换不需要的换行符,而不是直接删除它们,请在 RHS后面添加一个空格。或者\x1c
如果您希望它将换行符更改为 FS 字符。
搜索s///
和替换操作使用m
(“多行字符串”)和g
(“全局”)正则表达式修饰符。 g
对于几个使用正则表达式的工具(包括 sed)来说很常见,并导致正则表达式进行“全局”重复匹配,但m
特定于 perl:
来自man perlre
(搜索“修饰符”部分):
m
将匹配的字符串视为多行。也就是说,将 and 从匹配字符串第一行的开头和最后一行的结尾更改^
为$
匹配字符串中每行的开头和结尾。
注意 1:此脚本不关心“字段”分隔符是什么。它根本不查找或使用字段。如果字段分隔符是空格、制表符、冒号或其他任何内容(当然换行符除外),它也同样有效。
注意 2:如果不需要的换行符后面的任何字段以三位数字开头,则此方法将不起作用 - 例如123 London
。处理这个问题需要一个更复杂的脚本,一个能够解析和计算输入中的字段的脚本。
答案2
您可以使用awk
:
awk '{
while (NF < 5 && getline cmp) { $0=$0"<br>"cmp }
if (NF > 5) {
print "#ERROR"
count++
}
print
}
END{
if (count) {
print "FAILED "count" lines" > "/dev/stderr"
exit 8
}
}' FS=$'\x1c'
awk
非常擅长解析具有指定分隔符的字段NF
告诉当前行中的字段数- 您希望换行符始终出现在第二个字段中,然后您知道何时必须完成该行;只需阅读下一行并粘贴即可;也许用胶水标记
<br>
比用空格标记更好,这样就不会丢失信息。 - 如果换行符可以出现在该行的最后一个字段中,那么它会变得更加困难,但您的样本是安全的;不管怎样,这个脚本捕获了错误
亚斯塔?
答案3
使用乐(以前称为 Perl_6)
raku -e 'slurp.split("\x1C").join("\t").put;'
输入示例(制表符分隔):
~$ cat FS_test.txt
001 Baker St.
London 3 4 7
002 Penny Lane
Liverpool 88 5 7
~$ cat FS_test.txt | xxd
00000000: 3030 3109 4261 6b65 7220 5374 2e0a 4c6f 001.Baker St..Lo
00000010: 6e64 6f6e 0933 0934 0937 0a30 3032 0950 ndon.3.4.7.002.P
00000020: 656e 6e79 204c 616e 650a 4c69 7665 7270 enny Lane.Liverp
00000030: 6f6f 6c09 3838 0935 0937 0a ool.88.5.7.
将制表符转换为FS
(使用 MacOS 的查看十六进制xxd
):
~$ raku -e 'slurp.split("\t").join("\x1C").put;' baker_st_FS_test.txt | xxd
00000000: 3030 311c 4261 6b65 7220 5374 2e0a 4c6f 001.Baker St..Lo
00000010: 6e64 6f6e 1c33 1c34 1c37 0a30 3032 1c50 ndon.3.4.7.002.P
00000020: 656e 6e79 204c 616e 650a 4c69 7665 7270 enny Lane.Liverp
00000030: 6f6f 6c1c 3838 1c35 1c37 0a0a ool.88.5.7..
将制表符转换为-FS
并再次转换回来(FS
-to-tabs):
~$ cat FS_test.txt | raku -e 'slurp.split("\t").join("\x1C").put;' | raku -e 'slurp.split("\x1C").join("\t").put;'
001 Baker St.
London 3 4 7
002 Penny Lane
Liverpool 88 5 7
~$ cat FS_test.txt | raku -e 'slurp.split("\t").join("\x1C").put;' | raku -e 'slurp.split("\x1C").join("\t").put;' | xxd
00000000: 3030 3109 4261 6b65 7220 5374 2e0a 4c6f 001.Baker St..Lo
00000010: 6e64 6f6e 0933 0934 0937 0a30 3032 0950 ndon.3.4.7.002.P
00000020: 656e 6e79 204c 616e 650a 4c69 7665 7270 enny Lane.Liverp
00000030: 6f6f 6c09 3838 0935 0937 0a0a 0a ool.88.5.7...
请注意,每次拆分/合并往返都会在文件末尾添加一个额外的空行。通过.subst(/\n/, :nth(*))
在 Final 之前插入例行调用来摆脱这些问题.put
。或者通过.trim-trailing
在 slurped 文件上运行来消除所有尾随空格。 (另外,感谢@cas 提出的十六进制查看器想法)。
附录:Raku 的trans
后期例程也有效:
raku -e 'slurp.trans("\x1C" => "\t").put;'