【问题标题】:Python: Regex question / CSV parsing / Psycopg nested arraysPython:正则表达式问题/CSV 解析/Psycopg 嵌套数组
【发布时间】:2011-06-27 00:45:11
【问题描述】:

我在解析 Psycopg2 返回的嵌套数组时遇到问题。我正在处理的数据库返回可以将嵌套数组作为值的记录。 Psycopg 只解析这些值的外部数组。

我的第一种方法是将字符串拆分为逗号,但后来我遇到了一个问题,有时结果中的字符串也包含逗号,这使得整个方法无法使用。 我的下一次尝试是使用正则表达式在字符串中查找“组件”,但后来我注意到我无法检测到数字(因为数字也可以出现在字符串中)。

目前,这是我的代码:

import re
text = '{2f5e5fef-1e8c-43a2-9a11-3a39b2cbb45e,"Marc, Dirk en Koen",398547,85.5,-9.2, 62fe6393-00f7-418d-b0b3-7116f6d5cf10}'
r = re.compile('\".*?\"|[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}|^\d*[0-9](|.\d*[0-9]|,\d*[0-9])?$')
result = r.search(text)
if result:
    result = result.groups()

这样的结果应该是:

['2f5e5fef-1e8c-43a2-9a11-3a39b2cbb45e', 'Marc, Dirk en Koen', 398547, 85.5, -9.2, '62fe6393-00f7-418d-b0b3-7116f6d5cf10']

由于我希望此功能具有通用性,因此我无法确定参数的顺序。我只知道支持的类型是字符串、uuid、(有符号)整数和(有符号)小数。

我是否使用了错误的方法?或者谁能​​指出我正确的方向?

提前致谢!

【问题讨论】:

  • 字符串是否也可以包含 " 或 ',也许是那些的转义版本?
  • 理论上 psycopg2 应该解析嵌套数组。你能给我(fog@initd.org)一些测试数据吗?
  • 是的:这项工作:>>> cur.execute("select array[array['a','b'],array['c','d']];") > >> cur.fetchone() ([['a', 'b'], ['c', 'd']],) 如果你能提供一个失败的例子那就太好了。

标签: python regex arrays csv psycopg2


【解决方案1】:

从您的示例中,我觉得它类似于 ^{(?:(?:([^},"']+|"[^"]+"|'[^']+')(?:,|}))+(?<=})|})$。这并不完美,因为它允许“{foo,bar}baz}”,但如果这对你很重要,它可以被修复。

【讨论】:

    【解决方案2】:

    Python 的原生库应该做得很好。你试过了吗?

    http://docs.python.org/library/csv.html

    【讨论】:

      【解决方案3】:

      如果你能做断言,这会让你走上正轨。

      这个问题过于广泛,无法在单个正则表达式中完成。您正在尝试在全局匹配中同时验证和解析。但是您的预期结果需要在比赛后进行子处理。因此,最好编写一个更简单的全局解析器,然后对结果进行迭代以进行验证和修复(是的,您的示例中规定了修复)。

      两个主要的解析正则表达式是:

      1. 也去掉分隔符引用,只有 $2 包含数据,在 while 循环中使用,全局上下文
        /(?!}$)(?:^{?|,)\s*("|)(.*?)\1\s*(?=,|}$)/

      2. 我的首选,不去掉引号,只捕获 $1,可用于在数组或 while 循环中捕获,全局上下文
        /(?!}$)(?:^{?|,)\s*(".*?"|.*?)\s*(?=,|}$)/

      这是一个使用记录的正则表达式进行后处理(在 Perl 中)的示例:(edit: fix append trailing,)

      use strict; use warnings;
      
      my $str = '{2f5e5fef-1e8c-43a2-9a11-3a39b2cbb45e,"Marc, Dirk en Koen",398547,85.5,-9.2, 62fe6393-00f7-418d-b0b3-7116f6d5cf10}';
      
      my $rx = qr/ (?!}$) (?:^{?|,) \s* ( ".*?" | .*?) \s* (?=,|}$) /x;
      
      my $rxExpanded = qr/
               (?!}$)           # ASSERT ahead:  NOT a } plus end
               (?:^{?|,)        # Boundry: Start of string plus { OR comma
               \s*              # 0 or more whitespace
               ( ".*?" | .*?)   # Capture "Quoted" or non quoted data
               \s*              # 0 or more whitespace
               (?=,|}$)         # Boundry ASSERT ahead:  Comma OR } plus end
        /x;
      
      my ($newstring, $sucess) = ('[', 0);
      
      for my $field ($str =~ /$rx/g)
      {
         my $tmp = $field;
         $sucess = 1;
      
         if (  $tmp =~ s/^"|"$//g || $tmp =~ /(?:[a-f0-9]+-){3,}/ ) {
            $tmp = "'$tmp'";
         }
         $newstring .= "$tmp,";
      }
      if ( $sucess ) {
          $newstring =~ s/,$//;
          $newstring .= ']';
          print $newstring,"\n";
      }
      else {
          print "Invalid string!\n";
      }
      

      输出:
      ['2f5e5fef-1e8c-43a2-9a11-3a39b2cbb45e','Marc, Dirk en Koen',398547,85.5,-9.2,'6 2fe6393-00f7-418d-b0b3-7116f6d5cf10']

      【讨论】:

        【解决方案4】:

        CSV 方法似乎是最容易实现的:

        def parsePsycopgSQLArray(input):
            import csv
            import cStringIO
        
            input = input.strip("{")
            input = input.strip("}")
        
            buffer = cStringIO.StringIO(input)
            reader = csv.reader(buffer, delimiter=',', quotechar='"')   
        
            return reader.next() #There can only be one row 
        
        if __name__ == "__main__":
            text = '{2f5e5fef-1e8c-43a2-9a11-3a39b2cbb45e,"Marc, Dirk en Koen",398547,85.5,-9.2, 62fe6393-00f7-418d-b0b3-7116f6d5cf10}' 
            result = parsePsycopgSQLArray(text)
            print result
        

        感谢您的回复,他们很有帮助!

        【讨论】:

          【解决方案5】:

          改进了 Dirk 的回答。这可以更好地处理转义字符以及空数组的情况。也少了一次脱衣舞:

          def restore_str_array(val):
              """
              Converts a postgres formatted string array (as a string) to python
          
              :param val: postgres string array
              :return: python array with values as strings
              """
              val = val.strip("{}")
              if not val:
                  return []
              reader = csv.reader(StringIO(val), delimiter=',', quotechar='"', escapechar='\\')
              return reader.next()
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-10-18
            • 1970-01-01
            • 1970-01-01
            • 2010-10-23
            • 2012-05-18
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多