代码之家  ›  专栏  ›  技术社区  ›  Tomasz Zieliński

用于读取类似csv的行的python regex

  •  13
  • Tomasz Zieliński  · 技术社区  · 15 年前

    我想解析传入的类似csv的数据行。值用逗号分隔(逗号周围可能有前导空格和尾随空格),并且可以用“或”引号。例如-这是一个有效的行:

        data1, data2  ,"data3'''",  'data4""',,,data5,
    

    但这个是畸形的:

        data1, data2, da"ta3", 'data4',
    

    --引号只能由空格前置或尾随。

    这种格式错误的行应该被识别出来——最好是在行中以某种方式标记格式错误的值,但是如果regex不匹配整行,那么它也是可以接受的。

    我试图使用findall()的match()编写regex来解析它,但是我遇到的每个regex都有一些边缘情况的问题。

    所以,也许有分析类似事物经验的人可以帮我解决这个问题? (或者这对于regex来说太复杂了,我应该只编写一个函数)

    编辑1:

    csv 模块在这里不太有用:

        >>> list(csv.reader(StringIO('''2, "dat,a1", 'dat,a2',''')))
        [['2', ' "dat', 'a1"', " 'dat", "a2'", '']]
    
        >>> list(csv.reader(StringIO('''2,"dat,a1",'dat,a2',''')))
        [['2', 'dat,a1', "'dat", "a2'", '']]
    

    --除非可以调整?

    编辑2:一些语言的编辑-我希望现在英语更有效

    Edit3:谢谢你的回答,我现在很确定正则表达式不是一个好主意,因为(1)覆盖所有边缘情况可能很棘手(2)编写器输出不是规则的。写了这个,我决定检查一下上面提到的pyparsing并使用它,或者编写自定义的类似fsm的解析器。

    6 回复  |  直到 15 年前
        1
  •  7
  •   Community CDub    8 年前

    虽然可能会结合一些预处理,但使用 csv 模块、后处理和正则表达式的使用,您所述的要求与 猪瘟病毒 模块,也不可能与正则表达式一起使用(取决于您可能必须处理的嵌套引号的复杂性)。

    在复杂的分析案例中, pyparsing 总是一个很好的包装。如果这不是一次性的情况,它很可能会产生最直接和可维护的结果,但可能会花费一些额外的前期工作。但是,考虑到投资需要迅速得到回报,因为您节省了调试regex解决方案以处理角落案例的额外工作…

    您可以很容易地找到基于pyparsing的csv解析示例,使用 this question 也许足够让你开始了。

        2
  •  9
  •   Max Shawabkeh    15 年前

    csv 模块是正确的答案,一个可以做到这一点的regex是非常可行的:

    import re
    
    r = re.compile(r'''
        \s*                # Any whitespace.
        (                  # Start capturing here.
          [^,"']+?         # Either a series of non-comma non-quote characters.
          |                # OR
          "(?:             # A double-quote followed by a string of characters...
              [^"\\]|\\.   # That are either non-quotes or escaped...
           )*              # ...repeated any number of times.
          "                # Followed by a closing double-quote.
          |                # OR
          '(?:[^'\\]|\\.)*'# Same as above, for single quotes.
        )                  # Done capturing.
        \s*                # Allow arbitrary space before the comma.
        (?:,|$)            # Followed by a comma or the end of a string.
        ''', re.VERBOSE)
    
    line = r"""data1, data2  ,"data3'''",  'data4""',,,data5,"""
    
    print r.findall(line)
    
    # That prints: ['data1', 'data2', '"data3\'\'\'"', '\'data4""\'', 'data5']
    

    编辑: 要验证行,可以使用上面的regex进行少量添加:

    import re
    
    r_validation = re.compile(r'''
        ^(?:    # Capture from the start.
          # Below is the same regex as above, but condensed.
          # One tiny modification is that it allows empty values
          # The first plus is replaced by an asterisk.
          \s*([^,"']*?|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')\s*(?:,|$)
        )*$    # And don't stop until the end.
        ''', re.VERBOSE)
    
    line1 = r"""data1, data2  ,"data3'''",  'data4""',,,data5,"""
    line2 = r"""data1, data2, da"ta3", 'data4',"""
    
    if r_validation.match(line1):
        print 'Line 1 is valid.'
    else:
        print 'Line 1 is INvalid.'
    
    if r_validation.match(line2):
        print 'Line 2 is valid.'
    else:
        print 'Line 2 is INvalid.'
    
    # Prints:
    #    Line 1 is valid.
    #    Line 2 is INvalid.
    
        3
  •  4
  •   pwdyson    15 年前

    python有一个标准的库模块来读取csv文件:

    import csv
    
    reader = csv.reader(open('file.csv'))
    
    for line in reader:
        print line
    

    为您的示例输入此打印

    ['data1', ' data2 ', "data3'''", ' \'data4""\'', '', '', 'data5', '']
    

    编辑:

    您需要添加skipinitalspace=true,以便在您提供的额外示例的双引号之前允许空格。还不确定单引号。

    >>> list(csv.reader(StringIO('''2, "dat,a1", 'dat,a2','''), skipinitialspace=True))
    [['2', 'dat,a1', "'dat", "a2'", '']]
    
    >>> list(csv.reader(StringIO('''2,"dat,a1",'dat,a2','''), skipinitialspace=True))
    [['2', 'dat,a1', "'dat", "a2'", '']]
    
        4
  •  2
  •   John Machin Santi    15 年前

    无法给出答案,因为您尚未完全指定编写器正在使用的协议。

    它显然包含如下规则:

    如果一个字段包含任何逗号或单引号,请用双引号将其引起来。
    否则,如果该字段包含任何双引号,请用单引号将其引起来。
    注意:如果您在上述2个子句中交换了double和single,则结果仍然有效。
    否则不要引用。
    结果字段可以有空格(或其他空格?)预先或附加的。
    这样增加的字段被组装成一行,用逗号分隔,并由平台的新行(LF或CRLF)终止。

    未提及的是作者在这些情况下所做的:
    (0)字段同时包含单引号和双引号
    (1)字段包含前导非换行空格
    (2)字段包含尾随的非换行空格
    (3)字段包含任何换行符。
    如果作者忽略了这些情况,请指定您想要的结果。

    您还提到了“引号只能用空格来前置或尾随”——当然您的意思是也允许使用逗号,否则您的示例 'data4""',,,data5, 在第一个逗号上失败。

    你的数据是如何编码的?

        5
  •  1
  •   onaclov2000    15 年前

    这听起来可能太简单了,但实际上从你所要查找的字符串的外观来看,它包含[A-ZA-Z0-9][“']+[A-ZA-Z0-9],我的意思是,没有对数据进行深入测试,实际上你要查找的是字母之间的引号或双引号(或任何组合)(你也可以在其中添加数字)。

    根据你的要求,这真的不重要,它是一个csv,重要的是你的数据不符合。我相信只要搜索一个字母,然后搜索一个或多个“或”和另一个字母的组合。

    现在,您是想得到一个“数量”还是仅仅是一个包含它的行的打印输出,这样您就知道要返回哪个行并修复它了?

    很抱歉,我不知道python regex,但在perl中,它看起来像这样:

    # Look for one or more letter/number at least one ' or " or more and at least one    
    #  or more letter/number
    if ($line =~ m/[a-zA-Z0-9]+['"]+[a-zA-Z0-9]+/ig)
    {
        # Prints the line if the above regex is found
        print $line;
    
    }
    

    只要简单地把它转换成当你看一行的时候。

    如果我误解了这个问题,我很抱歉

    希望有帮助!

        6
  •  0
  •   knipknap    15 年前

    如果您的目标是将数据转换为XML(或JSON或YAML),请查看 this example 对于一个 Gelatin 产生以下输出的语法:

    <xml>
      <line>
        <column>data1</column>
        <column>data2  </column>
        <column>data3'''</column>
        <column>data4""</column>
        <column/>
        <column/>
        <column>data5</column>
        <column/>
      </line>
    </xml>
    

    注意,明胶还有一个python API:

    from Gelatin.util import compile, generate_to_file
    syntax = compile('syntax.gel')
    generate_to_file(syntax, 'input.csv', 'output.xml', 'xml')