Unix 文件在引号内包含换行符

Unix 文件在引号内包含换行符

我有一个 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

相关内容