处理带有嵌入式unix换行符的固定长度记录(Perl)

问题描述 投票:0回答:4

我收到的文本文件是固定长度字段和回车/换行符分隔记录(

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 转换。

建议?

perl newline
4个回答
3
投票

\x0A
 前面没有紧接时,为什么不替换 
\x0D

(用空格)
s/(?<!\x0D)\x0A/ /;

这使用了负回顾

将文件读入字符串(“slurp”它)可能是最安全的,因为不清楚这些 LF/CRLF 将如何“逐行”读取它 - 什么是操作系统上的“行”这是哪个处理的?所以

perl -0777 -wpE's/(?<!\x0D)\x0A/ /g' file

0777
命令开关有效地取消设置输入记录分隔符

这会打印出经过更改的文件。要就地更改它,请添加

-i
。请参阅链接的文档。


1
投票

您可以使用

-0777
读取整个输入,然后进行替换:

perl -0777pe 's/\r\n/\r/g;s/\n/ /g;s/\r/\r\n/g' badinput.txt

参数为:

  • p
    ,在每个“行”末尾输出
    $_
    的值
  • 0777
    将记录分隔符设置为 undef

Perl 命令行选项


0
投票

需要明确的是,您的记录由回车符/换行符对分隔(我为此编辑了您的问题)。您可以将行结尾设置为此,一次读取一条记录,并修改其中的任何内容。你不需要

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
。没什么大不了的。


-1
投票

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)
© www.soinside.com 2019 - 2024. All rights reserved.