替换大文件中带引号的字符串中的换行符

Replace newline in quoted strings in huge files

我有一些大文件,其中的值由竖线 (|) 符号分隔。 我们引用的字符串,但有时在引用的字符串之间有一个换行符。

我需要从 oracle 使用外部 table 读取这些文件,但在换行时他会给我错误。所以我需要用 space.

替换它们

我对这些文件执行了一些其他的 perl 命令以解决其他错误,所以我想在一行 perl 命令中找到一个解决方案。

我在 Whosebug 上发现了其他一些类似的问题,但它们的作用并不完全相同,而且我无法通过那里提到的解决方案找到解决我的问题的方法。

我试过但不起作用的声明:

perl -pi -e 's/"(^|)*\n(^|)*"/ /g' test.txt

示例文本:

4454|"test string"|20-05-1999|"test 2nd string"
4455|"test newline
in string"||"test another 2nd string"
4456|"another string"|19-03-2021|"here also a newline
"
4457|.....

应该变成:

4454|"test string"|20-05-1999|"test 2nd string"
4455|"test newline in string"||"test another 2nd string"
4456|"another string"|19-03-2021|"here also a newline "
4457|.....

听起来你想要一个像 Text::CSV_XS 这样的 CSV 解析器(通过你的 OS 的包管理器或最喜欢的 CPAN 客户端安装):

$ perl -MText::CSV_XS -e '
my $csv = Text::CSV_XS->new({sep => "|", binary => 1});
while (my $row = $csv->getline(*ARGV)) {
  $csv->say(*STDOUT, [ map { tr/\n/ /r } @$row ]) 
}' test.txt
4454|"test string"|20-05-1999|"test 2nd string"
4455|"test newline in string"||"test another 2nd string"
4456|"another string"|19-03-2021|"here also a newline "

这个one-liner使用|作为字段分隔符而不是正常的逗号来读取每条记录,并且对于每个字段,用空格替换换行符,然后打印出转换后的记录。

在您的具体情况下,您还可以考虑使用 GNU or .

的解决方法

一个命令看起来像

awk 'NR==1 {print;next;} /^[0-9]{4,}\|/{print "\n" [=10=];next;}1' ORS="" file > newfile

ORS(输出记录分隔符)设置为空字符串,这意味着 \n 仅添加在以四位或更多数字开头后跟 | 字符(与^[0-9]{4,}\| POSIX ERE 模式)。

GNU 命令看起来像

sed -i ':a;$!{N;/\n[0-9]\{4,\}|/!{s/\n/ /;ba}};P;D' file

这会将连续的两行读入模式 space,一旦第二行不是以四位数字开头后跟 | 字符(请参阅 [0-9]\{4\}| POSIX BRE 正则表达式),两者之间的或更多换行符被替换为 space。重复搜索和替换,直到没有匹配项或文件结束。

使用,如果文件很大但它仍然可以装入内存,你可以使用一个短的

perl -0777 -pi -e 's/\R++(?!\d{4,}\|)/ /g'  <<< "$s"

使用 -0777,您 slurp the file\R++(?!\d{4,}\|) 模式匹配任何一个或多个换行符 (\R++) 后跟不跟四位或更多位的数字| 个字符。需要 ++ 所有格量词才能进行 (?!...) 否定前瞻,以禁止回溯到换行符匹配模式。

使用您展示的示例,这可以在 awk 程序中简单地完成。在 GNU awk 中编写和测试,应该在任何 awk 中工作。即使在大文件上,这也应该工作得很快(比将整个文件放入内存更好,已经提到 OP 可能会在大文件上使用它)。

awk 'gsub(/"/,"&")%2!=0{if(val==""){val=[=10=]} else{print val [=10=];val=""};next} 1' Input_file

说明:为以上添加详细说明。

awk '                                ##Starting awk program from here.
gsub(/"/,"&")%2!=0{                  ##Checking condition if number of " are EVEN or not, because if they are NOT even then it means they are NOT closed properly.
  if(val==""){ val=[=11=]            }   ##Checking condition if val is NULL then set val to current line.
  else       {print val [=11=];val=""}   ##Else(if val NOT NULL) then print val current line and nullify val here.
  next                               ##next will skip further statements from here.
}
1                                    ##In case number of " are EVEN in any line it will skip above condition(gusb one) and simply print the line.
' Input_file                         ##Mentioning Input_file name here.