我收到的文本文件是固定长度字段和回车/换行符分隔记录(
CRLF
)。最近,其中一个文本字段开始在记录中显示换行符 (LF
)。这显然在我们的 unix 服务器上造成了一些问题。
我想简单地在文件中查找
LF
的使用并将其替换为单个空格,但这显然会干扰Windows换行符。
我已经尝试过
tr
和perl
但似乎不太正确:
cat badinput.txt | perl -p -e 's/\x0D\x0A/\x0D/' | perl -p -e 's/\0A/ /' | perl -p -e 's/\x0D/\x0D\x0A/' > goodoutput.txt
这个想法是
CRLF
替换为 CR
LF
替换为
CR
替换为 CRLF
由于某种原因,我还没有完全理解 CR -> CRLF 转换。
建议?
您可以使用
-0777
读取整个输入,然后进行替换:
perl -0777pe 's/\r\n/\r/g;s/\n/ /g;s/\r/\r\n/g' badinput.txt
参数为:
p
,在每个“行”末尾输出 $_
的值0777
将记录分隔符设置为 undef需要明确的是,您的记录由回车符/换行符对分隔(我为此编辑了您的问题)。您可以将行结尾设置为此,一次读取一条记录,并修改其中的任何内容。你不需要
chomp
或任何特殊的线尾装甲。
这是一个示例文件,其中两个
NL
文字之间有一个换行符,行结尾为 CRLF(尽管 Stackoverflow 可能不会向您显示这一点):
one,two,three
uno,dos,tress
dog,cat,NL
NL
one,two,again
它看起来是什么样子(注意
4e 4c 0a 4e 4c
)
$ hexdump -C badinput.txt
00000000 6f 6e 65 2c 74 77 6f 2c 74 68 72 65 65 0d 0a 75 |one,two,three..u|
00000010 6e 6f 2c 64 6f 73 2c 74 72 65 73 73 0d 0a 64 6f |no,dos,tress..do|
00000020 67 2c 63 61 74 2c 4e 4c 0a 4e 4c 0d 0a 6f 6e 65 |g,cat,NL.NL..one|
00000030 2c 74 77 6f 2c 61 67 61 69 6e 0d 0a |,two,again..|
0000003c
现在我需要阅读此内容,以便行结尾为 CRLF。我将特殊变量
$/
(输入记录分隔符)设置为我想要的任何值。现在,裸露的 LF 不再是问题,因为它只是第 3 行的一部分,并且由于我没有对 CR 做任何操作,所以行结尾仍然是 CRLF(您可能不会在这里看到):
$ perl -ne 'print qq($. $_) } BEGIN { $/ = qq(\xd\xa) ' badinput.txt
1 one,two,three
2 uno,dos,tress
3 dog,cat,NL
NL
4 one,two,again
接下来,我替换所有不在行尾的 NL(因此,仅替换内部的 NL)。这使用负向前视来检查字符串的绝对结尾:
(?!\z)
,但其他类型的模式也可以工作(例如zdim的答案):
$ perl -ne 's/\xa(?!\z)/ /g; print qq($. $_) } BEGIN { $/ = qq(\xd\xa) ' badinput.txt
1 one,two,three
2 uno,dos,tress
3 dog,cat,NL NL
4 one,two,again
由于
-n
实际上只是将参数包装到 -e
,我可以使用它的开始和结束大括号来打开和关闭不同的东西。我自己关闭了隐式 while
,并使用剩余的隐式右大括号来表示 BEGIN
。没什么大不了的。
Perl 的文本::CSV ++。
但是,如果需要,Perl/PCRE 正则表达式
(\015?\012)|\015
将匹配任何常见的行尾结构: LF、CRLF、CR 。如果你想让整个文件 CRLF:
# slurp the file into $file_text
my $preferred_eol = "\015\012";
$file_text =~ s/(\015?\012)|\015/$preferred_eol/ge;
# print $file_text to new file (or close & then overwrite the original)