【问题标题】:Replace new line character between double quotes with space用空格替换双引号之间的换行符
【发布时间】:2014-12-11 22:20:41
【问题描述】:

我想逐行读取数据,无论在哪里找到双引号,我都想用空格替换换行符,直到遇到第二个双引号 喜欢

090033ec82b13639,CPDM Initiated,Logistical,"There corrected.",Gul Y Serbest,Urology
090033ec82ae0c07,Initiated,NA,"To   local testing
Rohit  3 to 4.",Julienne B Orr,Oncology
090033ec82b35fd0,Externally Initiated,NA,regulatory agency requests,Kenneth A Lord,Oncology

就像上面的数据第二行一样,它在第三行找到双引号(打开)和关闭双引号,所以我们需要将这些行合并为一个空格,如下所示:

090033ec82b13639,CPDM Initiated,Logistical,"There corrected.",Gul Y Serbest,Urology
090033ec82ae0c07,Initiated,NA,"To   local testing Rohit  3 to 4.",Julienne B Orr,Oncology
090033ec82b35fd0,Externally Initiated,NA,regulatory agency requests,Kenneth A Lord,Oncology

【问题讨论】:

    标签: regex bash shell unix awk


    【解决方案1】:

    你可以使用这个gnu-awk one-liner

    awk -v RS='"[^"]*"' -v ORS= '{gsub(/\n/, " ", RT); print $0  RT}' file
    090033ec82b13639,CPDM Initiated,Logistical,"There corrected.",Gul Y Serbest,Urology
    090033ec82ae0c07,Initiated,NA,"To   local testing Rohit  3 to 4.",Julienne B Orr,Oncology
    090033ec82b35fd0,Externally Initiated,NA,regulatory agency requests,Kenneth A Lord,Oncology
    
    • RS='"[^"]*"' - 输入记录分隔符设置为正则表达式 '"[^"]*"'
    • -v ORS= - 输出记录分隔符设置为空
    • gsub(/\n/, " ", RT) - 用Input Record Separator 匹配的文本中的空格替换换行符

    这是一个 perl 单行代码

    perl -0pe 's/"[^\n"]*"(*SKIP)(*F)|("[^"\n]*)\n([^"]*")/$1 $2/g' file
    090033ec82b13639,CPDM Initiated,Logistical,"There corrected.",Gul Y Serbest,Urology
    090033ec82ae0c07,Initiated,NA,"To   local testing Rohit  3 to 4.",Julienne B Orr,Oncology
    090033ec82b35fd0,Externally Initiated,NA,regulatory agency requests,Kenneth A Lord,Oncology
    

    【讨论】:

    • 我以前听说过perl 动词模式,但我自己从未真正使用过它+1
    【解决方案2】:

    这将适用于您示例中的简单案例:

    $ perl -00pe 's/(\n[^"]*"[^"]+)\n(.+?")/$1 $2/gm' file 
    090033ec82b13639,CPDM Initiated,Logistical,"There corrected.",Gul Y Serbest,Urology
    090033ec82ae0c07,Initiated,NA,"To   local testing Rohit  3 to 4.",Julienne B Orr,Oncology
    090033ec82b35fd0,Externally Initiated,NA,regulatory agency requests,Kenneth A Lord,Oncology
    

    注意事项

    • 这会将整个文件加载到内存中,这可能会出现问题,具体取决于文件的大小。
    • 它不处理超过一行的开引号。

    说明

    • -00 :slurp 文件,将其视为单个字符串。
    • -pe :在应用-e 给出的脚本后打印每个输入行(这里是单个“行”,因为-00)。
    • (\n[^"]*"[^"]+)\n(.+?") :匹配换行符(用于指示行首),后跟尽可能多的非" ([^"]*),然后是",后跟仅非" 字符直到下一个换行符([^"]+\n),然后是第一个引号之前的所有内容。括号在那里,所以我们可以捕获匹配的字符串。
    • $1 $2 :这是替换,它将打印前两个捕获的组,因此我们将匹配的模式替换为第一个组,一个空格,然后是第二个。

    • gmg 使替换成为全局,m 允许多行字符串。

    【讨论】:

      【解决方案3】:

      这条单线就可以了:

      perl -F'' -0 -ane ' foreach $char(@F){  $char eq q(") && {$seen= $seen ? 0 : 1}; $seen  && $char eq "\n" && { $char=" "}; print $char}'
      

      或:

      perl -F'' -0 -ane 'map {$_ eq q(") && {$seen=$seen?0:1}; $seen && $_ eq "\n" &&{$_=" "}; print} @F'
      

      在行动:

      $ perl -F'' -0 -ane ' foreach $char(@F){  $char eq q(") && {$seen= $seen ? 0 : 1}; $seen  && $char eq "\n" && { $char=" "}; print $char}' file
      090033ec82b13639,CPDM Initiated,Logistical,"There corrected.",Gul Y Serbest,Urology
      090033ec82ae0c07,Initiated,NA,"To   local testing Rohit  3 to 4.",Julienne B Orr,Oncology
      090033ec82b35fd0,Externally Initiated,NA,regulatory agency requests,Kenneth A Lord,Oncology
      

      【讨论】:

      • 输出应该是 3 行而不是 2
      • 我还在我的回答中提供了一个perl one-liner,它完全基于正则表达式。
      【解决方案4】:

      Perl 的救援:

      #!/usr/bin/perl
      use warnings;
      use strict;
      
      use Text::CSV;
      my $csv = 'Text::CSV'->new({ binary => 1,
                                   eol => "\n",
                                 })
          or die "Cannot use CSV: " . 'Text::CSV'->error_diag;
      
      open my $CSV, '<:utf8', shift or die $!;
      while (my $row = $csv->getline($CSV)) {
          s/\n/ /g for @$row;
          $csv->print(*STDOUT, $row);
      }
      

      运行时给出预期的输出

      remove-newlines.pl input.csv > output.csv
      

      【讨论】:

        【解决方案5】:

        使用(我认为)bashism 的解决方案(不是 POSIX,它不应该在 bash 以外的其他 shell 上工作):

        function fixmylines { 
          local line fullline
          while read line ; do 
            if [[ "$line" =~ ^[0-9a-f]{16}, ]] ; then
              [ "$fullline" ] && echo "$fullline"
              fullline="$line"
            else
              fullline+=" $line"
            fi
          done
          echo "$fullline"
        }
        

        那么你可以将你的数据通过管道传递给这个函数(“| fixmylines”)。

        注意:它使用正则表达式“^[0-9a-f]{16}”来确定行首

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-03-12
          • 2014-10-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-08
          • 2023-03-07
          相关资源
          最近更新 更多