用于CSV校正的Perl REGEX模式

时间:2018-06-22 18:20:09

标签: regex linux perl

perl -i -pe 's/(,\h*"[^\n"]*)\n/$1 /g' /opt/data-integration/transfer/events/processing/Master_Events_List.csv

这是怎么回事?我尝试过翻译,但是有点模糊。可能会在这里返回哪些示例?

3 个答案:

答案 0 :(得分:3)

首先,请勿尝试使用正则表达式来操作CSV(或XML或HTML)。尽管CSV看起来很简单,但它可能很微妙。而是使用Text::CSV。例外是,如果您的CSV格式不正确,并且您正在对其进行修复。

现在,对于您的正则表达式正在做什么。首先,让我们将其从s//转换为s{}{},这在眼睛上会更容易一些,并使用\x,这样我们就可以将内容隔开一些。

s{
    # Capture to $1
    (
        # A comma.
        ,
        # 0 or more `h` "horizontal whitespace": tabs and spaces
        \h*
        # A quote.
        "
        # 0 or more of anything which is not a quote or newline.
        [^\n"]*
    )
    # A newline (not captured)
    \n
}
# Put the captured bit in with a space after it.
# The `g` says to do it multiple times over the whole string.
{$1 }gx

它将foo, "bar\n更改为foo, "bar。我猜测这会将CSV中带有换行符的文本字段变成只有空格的文本字段。

foo, "first
field", "second
field"

将成为

foo, "first field", "second field"

使用Text :: CSV可以更好地处理此问题。我怀疑转换的目的是帮助无法处理换行符的CSV解析器。 Text::CSV can with a little coercing

#!/usr/bin/env perl

use strict;
use warnings;
use v5.10;
use autodie;

use Text::CSV;
use IO::Scalar;
use Data::Dumper;

# Pretend our scalar is an IO object so we can use `getline`.
my $str = qq[foo, "bar", "this\nthat"\n];
my $io = IO::Scalar->new(\$str);

# Configure Text::CSV
my $csv = Text::CSV->new({
    # Embedded newlines normally aren't allowed, this tells Text::CSV to
    # treat the content as binary instead.
    binary=> 1,

    # Allow spaces between the cells.
    allow_whitespace => 1
});

# Use Text::CSV->getline() to do the parsing.
while( my $row = $csv->getline($io) ) {
    # Dump the contents of the row
    say Dumper $row;
}

它将正确解析该行及其嵌入的换行符。

$VAR1 = [
          'foo',
          'bar',
          'this
that'
        ];

答案 1 :(得分:0)

将此内容编辑为第二本Schwern(也被赞成):正则表达式似乎不适合处理CSV。

对于正则表达式,让我们对其进行剖析。从顶层开始:

's/(,\h*"[^\n"]*)\n/$1 /g'

s/part1/part2/g表示“将第一部分替换为第二部分”。

现在让我们检查“第一部分”:

(,\h*"[^\n"]*)\n

括号括起一个组。只有一个组,所以它成为组号1。我们将在下一步继续讨论。

然后,检查https://perldoc.perl.org/perlrebackslash.html以获取字符类的解释。 \h是水平空格,\n是逻辑换行符。

该组中的表达式表示:“以逗号开头,然后是任意数量的水平空白字符,然后是除换行符和引号之外的任何内容;最后,必须有尾随的换行符”。因此,它基本上是由csv字段引起的逗号。

最后,“第二部分”显示为:

$1

这只是对前面捕获的组号1的引用,后跟一个空格。

总体而言,整个表达式将替换不以引号终止的尾随字符串字段,并删除其换行符。

答案 2 :(得分:0)

在被伪装成记录结尾的引用字段中修复换行符的最佳方法:

首先,请勿尝试通过模块操作CSV(或XML或HTML)。尽管CSV似乎有些棘手,但它却非常简单。不要使用Text :: CSV。而是将替代正则表达式与回调一起使用。

此外,您可以使用正则表达式来正确地解析一个CSV而不替换
换行符,但您可能希望使用Perl对其进行修复以便用于其他某种语言。

正则表达式(带有修饰符)

(id1, id2, date)

解释

/((?:^|,|\r?\n))\s*(?:("[^"\\]*(?:\\[\S\s][^"\\]*)*"[^\S\r\n]*(?=$|,|\r?\n))|([^,\r\n]*(?=$|,|\r?\n)))/

注意-这需要一个脚本。

Perl示例

 (                                  # (1 start), Delimiter (comma or newline)
      (?: ^ | , | \r? \n )
 )                                  # (1 end)
 \s*                                # Leading optional whitespaces ( this is for trim )
                                    # ( if no trim is desired, remove this, add 
                                    #  [^\S\r\n]* to end of group 1 )
 (?:
      (                                  # (2 start), Quoted string field
           "                                  # Quoted string
           [^"\\]* 
           (?: \\ [\S\s] [^"\\]* )*
           "
           [^\S\r\n]*                         # Trailing optional horizontal whitespaces
           (?= $ | , | \r? \n )               # Delimiter ahead (EOS, comma or newline)
      )                                  # (2 end)
   |                                   # OR
      (                                  # (3 start), Non quoted field
           [^,\r\n]*                          # Not comma or newline
           (?= $ | , | \r? \n )               # Delimiter ahead (EOS, comma or newline)
      )                                  # (3 end)
 )

输出

use strict;
use warnings;

$/ = undef;

sub RmvNLs {
   my ($delim, $quote, $non_quote) = @_;
   if ( defined $non_quote ) {
      return $delim . $non_quote;
   }
   $quote =~ s/\s*\r?\n/ /g;
   return $delim . $quote;
}

my $csv = <DATA>;

$csv =~ s/
     (                                  # (1 start), Delimiter (comma or newline)
          (?: ^ | , | \r? \n )
     )                                  # (1 end)
     \s*                                # Leading optional whitespaces ( this is for trim )
                                        # ( if no trim is desired, remove this, add [^\S\r\n]* to end of group 1 )
     (?:
          (                                  # (2 start), Quoted string field
               "                                  # Quoted string
               [^"\\]* 
               (?: \\ [\S\s] [^"\\]* )*
               "
               [^\S\r\n]*                         # Trailing optional horizontal whitespaces
               (?= $ | , | \r? \n )               # Delimiter ahead (EOS, comma or newline)
          )                                  # (2 end)
       |                                   # OR
          (                                 # (3 start), Non quoted field
               [^,\r\n]*                          # Not comma or newline
               (?= $ | , | \r? \n )               # Delimiter ahead (EOS, comma or newline)
          )                                  # (3 end)
     )
   /RmvNLs($1,$2,$3)/xeg;

print $csv;


__DATA__
497,50,2008-08-02T16:56:53Z,469,4,

"foo bar 
foo

bar"
518,153,2008-08-02T17:42:28Z,469,2,"foo bar
bar"



hello
world
"asdfas"

ID,NAME,TITLE,DESCRIPTION,,
PRO1234,"JOHN SMITH",ENGINEER,"JOHN HAS BEEN WORKING

HARD ON BEING A GOOD

SERVENT."



PRO1235,   "KEITH SMITH",ENGINEER,"keith has been working

hard on being a good

servent."
PRO1235,"KENNY SMITH",,"keith has been working

hard on being a good

servent."
PRO1235,"RICK SMITH",,,     #