【问题标题】:How can I replace line-feed in csv quoted fields with a blank?如何用空白替换 csv 引用字段中的换行符?
【发布时间】:2018-11-17 19:54:00
【问题描述】:

有一个大的 csv 文件,在引用的列中有一些换行符。我需要运行一个 shell 脚本,sed、awk、perl 都可以,并且只用空格替换引号内的换行符。必须保留行尾,我不知道列数或哪些字段可能有这些嵌入的换行符。

对文件的进一步检查表明这是 cat -v test_lf.csv 的结果

"NORTH ?M-^@?OLMSTED"
"PORT?M-^@?ST?M-^@?LUCIE"

在 csv 文件中,它在 excel 中显示了一个换行符,其中 ?M-^@?是。

我想用 tr 来替换空格。我该怎么做?那个顺序是什么?

我现在发现文件的一小部分在字符和十六进制中如下所示。

13:20:59 [woo:~/Development/scripts] > tail -n 8 test_lf.csv | head -n 1 | od -x
0000000      3431    3136    3439    3039    2c39    542c    4c45    3543
0000020      5f36    3430    2c47    4c46    332c    3934    3338    312c
0000040      3634    3931    3934    3930    222c    4f50    5452    80e2
0000060      53a8    e254    a880    554c    4943    2245    462c    2c4c
0000100      3433    3839    2c33    3737    2c32    3237    2c37    3535
0000120      2c2c    5441    334c    2c51    6e43    7463    222c    6f4e
0000140      80e2    4da8    6773    2c22    2c34    3832    312c    0d35
0000160      000a                                                        
0000161
13:21:50 [woo:~/Development/scripts] > tail -n 8 test_lf.csv | head -n 1 | od -c
0000000    1   4   6   1   9   4   9   0   9   ,   ,   T   E   L   C   5
0000020    6   _   0   4   G   ,   F   L   ,   3   4   9   8   3   ,   1
0000040    4   6   1   9   4   9   0   9   ,   "   P   O   R   T   
      **
0000060   **   S   T   
      **  **   L   U   C   I   E   "   ,   F   L   ,
0000100    3   4   9   8   3   ,   7   7   2   ,   7   2   7   ,   5   5
0000120    ,   ,   A   T   L   3   Q   ,   C   n   c   t   ,   "   N   o
0000140    
      **  **   M   s   g   "   ,   4   ,   2   8   ,   1   5  \r
0000160   \n  

我在 Mac 上,文件是 utf-8,我的语言环境是 utf-8。看起来字节被交换了(小端),所以十六进制 3431 3136 在字符表示中是 1463。所以,从这个输出中的字节 60 开始,我们有: 某物,S,T,空白,某物,某物,L,十六进制是: 53a8 e254 a880 554c,53 是 S,54 是 T,4c 是 L。因此,在 T 和 L 之间有一个 e2、a8、80 序列。这会在 Excel 电子表格字段中产生换行符。
如何查找这些字节并将其替换为空格?

【问题讨论】:

  • 你已经试过了......究竟是什么?
  • awk -F"\"" '!$NF{print;next}{printf("%s ", $0)}' 文件但这是摆脱终端换行符,它看起来喜欢。
  • 那么该 CSV 需要做什么?要更正它,有glenn's answer 但请注意,您最好使用该模块来读取文件(如答案所示),因为该模块没有换行符问题,然后按您的意愿处理。
  • 它必须通过一个大型 R 程序运行,并且必须首先将其作为文本读取,然后应用大量正则表达式,然后解析作为 csv 传递的行。由于嵌入的换行符,以文本形式读取会中断。就 R 分析而言,它们并不重要,重要的是它们中的信息。
  • 好的。然后@glenn answer 修复了这些换行符。 (也许仍然考虑是否可以将一些处理卸载到 Perl,这对于各种字符串修饰来说要好得多。)

标签: bash perl csv awk sed


【解决方案1】:

在大量读取 csv 文件的十六进制转储后,查看 Mac 上的 Numbers 和 PC 上的 Excel 处理嵌入中断的方式的差异,似乎一种简单的便携式方法来定位和更改中断的编码不是值得努力。如果包含中断的数据字段用引号引起来,那么 Excel 将像 R 中的 read.csv 一样读取它们。

【讨论】:

    【解决方案2】:

    假设引号字符是双引号 " 并且转义字符也是双引号,如果在双引号的数量为偶数时将所有换行符转换为空格,则可以使用 sed图案空间。当数字为奇数时,您只需追加下一行即可。

    sed ':a;/^\([^"]*"[^"]*"\)*[^"]*$/!{N;ba};y/\n/ /' file.csv
    

    详情:

    :a    # define the label "a"
    /^\([^"]*"[^"]*"\)*[^"]*$/! # if not an even number of quotes
    {
        N  # append the next line to the pattern space
        ba # go to label "a"
    }
    y/\n/ / # translate all line-feeds to spaces
    

    如果引号没有很好地平衡,默认行为是不继续最后引用的部分。你可以改写:

    sed ':a;${y/\n/ /;s/$/"/};/^\([^"]*"[^"]*"\)*[^"]*$/!{N;ba};y/\n/ /' file.csv
    

    【讨论】:

      【解决方案3】:

      我会使用 Perl 模块 Text::CSV

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      use feature qw/say/;
      use open IO => ':encoding(utf8)';
      use open ':std';
      use Text::CSV;
      
      my $file = shift @ARGV;
      open my $fh, "<", $file or die "cannot open $file: $!\n";
      
      my $csv = Text::CSV->new({binary => 1});
      
      while (my $row = $csv->getline($fh)) {
          my @no_newlines = map {s/\n/ /g; $_} @$row;
          $csv->combine(@no_newlines);
          say $csv->string();
      }
      
      close $fh;
      

      然后你可以像这样运行它:

      /path/to/csvfixer.pl file.csv > fixed.csv
      

      【讨论】:

      • 从哪里获得 Text/CSV.pm ?
      • @JohnWooten 每个操作系统都有一个易于安装模块的系统(因此您不必使用来自 CPAN 的压缩包!)。在 Linux 上有 cpan(或 cpanm,更简单),或者您可以获取您使用的发行版的软件包(例如,对于 RHEL,它是 Perl-Text-CSV...rpm)。 Windows 也一样(但我不熟悉安装工具。)
      • 在我的机器上找到了 ppm 并在使用 Text::CSV 之前做了一个 push(@INC,path);得到这个结果 fix.pl test_lf.csv > fixed.csv 使用 /c 修饰符在 ./fix.pl 第 8 行没有 /g 是没有意义的。正则表达式修饰符“/l”和“/a”在 ./fix 处是互斥的.pl 第 8 行,在行尾 正则表达式修饰符“/l”可能不会在 ./fix.pl 第 8 行出现两次,在 ./fix.pl 第 8 行的行尾出现语法错误,靠近“/)” BEGIN not错误后安全——编译在 ./fix.pl 第 9 行中止。
      • @JohnWooten 尝试正常安装模块(ActiveState?)。你绝对不需要也不需要使用push @INC...
      猜你喜欢
      • 1970-01-01
      • 2020-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-18
      • 2015-01-06
      • 2016-04-08
      相关资源
      最近更新 更多