替换大文件中带引号的字符串中的换行符
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 sed or awk.
的解决方法
一个awk命令看起来像
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 命令看起来像
sed -i ':a;$!{N;/\n[0-9]\{4,\}|/!{s/\n/ /;ba}};P;D' file
这会将连续的两行读入模式 space,一旦第二行不是以四位数字开头后跟 |
字符(请参阅 [0-9]\{4\}|
POSIX BRE 正则表达式),两者之间的或更多换行符被替换为 space。重复搜索和替换,直到没有匹配项或文件结束。
使用perl,如果文件很大但它仍然可以装入内存,你可以使用一个短的
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.
我有一些大文件,其中的值由竖线 (|) 符号分隔。 我们引用的字符串,但有时在引用的字符串之间有一个换行符。
我需要从 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 sed or awk.
的解决方法一个awk命令看起来像
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 命令看起来像
sed -i ':a;$!{N;/\n[0-9]\{4,\}|/!{s/\n/ /;ba}};P;D' file
这会将连续的两行读入模式 space,一旦第二行不是以四位数字开头后跟 |
字符(请参阅 [0-9]\{4\}|
POSIX BRE 正则表达式),两者之间的或更多换行符被替换为 space。重复搜索和替换,直到没有匹配项或文件结束。
使用perl,如果文件很大但它仍然可以装入内存,你可以使用一个短的
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.