我有一个 CSV 奇怪的文件,其中引号内有引号和换行符,而单列中则没有。现在我需要将该列标识为“换行符”作为一列,并用一些分隔符替换换行符。
我有 3 列,第三列将包含一些带有双引号和每个特殊字符的 HTML 文本。但双引号是用双引号转义的,比如"<This ""is"" string>"
.
输入:
ID、姓名、文字
"1","abc","Line 1"
"2","def","Line2
""line2"",line2"
"3","ghi","line3"
输出:
ID, Name, text
"1","abc","Line 1"
"2","def","Line2 ""line2"",line2"
"3","ghi","line3"
答案1
您的文件没有真正的问题。它嵌入了换行符和双引号。 CSV 解析器能够正确处理它。使用转义双引号"
(同时双引号字段)是转义 CSV 文件中嵌入双引号的正确方法。
要更换嵌入式CSV 文件中带有@
字符的换行符,您可以这样做:
$ csvformat -M '@' file.csv | tr '\n@' '@\n'
1,abc,Line 1
2,def,"Line2@""line2"",line2"
3,ghi,line3
这使用csvformat
来自csvkit 工具箱。它是一个合适的 CSV 解析器,能够重新格式化 CSV 文件。
上面的命令管道首先替换所有换行符不是嵌入@
角色。然后我用来tr
交换剩余的换行符和@
字符,最终得到一个嵌入换行符为 的 CSV 文件@
。
这依赖于文件中的原始数据不包含字符的事实@
。
如果您想要使用空格而不是换行符最初所在位置的标记,请使用tr '\n@' ' \n'
而不是tr
上面显示的:
$ csvformat -M '@' file.csv | tr '\n@' ' \n'
1,abc,Line 1
2,def,"Line2 ""line2"",line2"
3,ghi,line3
请注意,如果有的话,这将使重新插入原始换行变得极其困难其他数据中的空格(如第一行第三个字段中的空格)。
您是否希望csvformat
不删除所有不必要的双引号,然后将其与以下命令一起使用-U 1
:
$ csvformat -U 1 -M '@' file.csv | tr '\n@' ' \n'
"1","abc","Line 1"
"2","def","Line2 ""line2"",line2"
"3","ghi","line3"
使用 Miller 得到更简单的答案(2022 年 10 月):
$ cat file
ID,Name,text
"1","abc","Line 1"
"2","def","Line2
""line2"",line2"
"3","ghi","line3"
$ mlr --csv put '$text = gsub($text,"\n"," ")' file
ID,Name,text
1,abc,Line 1
2,def,"Line2 ""line2"",line2"
3,ghi,line3
这会读取 CSV 文件(假设有正常的标头)并用空格gsub()
替换字段中的任何换行符。text
答案2
您可以尝试使用 sed :
sed '
:A
2,$ {
/[^"]\"$/! {
N
bA
}
s/\n//g
}
' infile
如果最后一个字符是 ",则捕获从 2 到结尾的每一行。
如果不是,则获取换行符并重新启动循环。
在循环结束时,删除每个“\n”。
答案3
您可以使用 GNU 版本的sed
,利用扩展的正则表达式支持来执行此操作,如下所示:
命令行:
$ sed -Ee '
1b
/^("[^"]*"[^"]*)*$/!{
N;s/\n/ /;s/^/\n/;D
}
' input.csv
结果:
ID,Name,Text
"1","abc","Line 1"
"2","def","Line2 ""line2"",line2"
"3","ghi","line3"
解释:
-E
打开扩展正则表达式模式。1b
会将标头按原样发送到标准输出。/^("[^"]*"[^"]*)*$/
将匹配与双引号完全平衡的行。- 因此,当我们否定它时,我们会得到不平衡的行,IOW,我们需要在后续行中寻找它们的结束双引号。
- 我们读取下一行并将其附加到模式空间 ,
N
并删除换行符。 - 我们重复这个过程直到模式空间平衡。
POSIX
sed
您需要对上面的内容进行一些更改:
$ sed -e '
1b
/^\("[^"]*"[^"]*\)*$/b
N;s/\n/ /;H;s/.*//;x;D
' input.csv
答案4
使用 Raku(以前称为 Perl_6)
raku -MText::CSV -e 'my $csv=Text::CSV.new; .perl.put for $csv.getline_all(open($*ARGFILES, :r, :!chomp));'
输入示例:
ID, Name, text
"1","abc","Line 1"
"2","def","Line2
""line2"",line2"
"3","ghi","line3"
示例输出:
$["ID", "Name", "text"]
$["1", "abc", "Line 1"]
$["2", "def", "Line2\n\"line2\",line2"]
$["3", "ghi", "line3"]
您可以使用 Raku 编程语言和专用模块(例如Text::CSV
)来处理引号和嵌入换行符。为了可视化\n
我添加了一个调用的角色.perl
(仅供参考,.raku
也可以)。将映射添加到字段中,以将嵌入的换行符更改为下划线(附加代码,如下):
raku -MText::CSV -e 'my $csv=Text::CSV.new; .put for $csv.getline_all(open($*ARGFILES, :r, :!chomp)).map(*.subst("\n","_", :g));'
更新输出 (1):
ID Name text
1 abc Line 1
2 def Line2_"line2",line2
3 ghi line3
从OP的最初发布中确实不清楚是否需要(或不需要)“双”双引号。只需将调用添加回.perl
上面的代码即可得到转义的双引号(如下),这可能更理想:
更新输出 (2):
"ID Name text"
"1 abc Line 1"
"2 def Line2_\"line2\",line2"
"3 ghi line3"
https://modules.raku.org/dist/Text::CSV:cpan:HMBRAND
https://github.com/Tux/CSV
https://raku.org