【问题标题】:How to count occurrences of separator in string excluding those in quotes如何计算字符串中分隔符的出现次数,不包括引号中的分隔符
【发布时间】:2015-07-19 05:40:20
【问题描述】:

我有一个带有逗号分隔值的文件,如下所示:

1.42104E+16,220899,1,,e-page remote auto,,,"Allied Martian Banks, P.L.C.",Moon,MN,,
1.42105E+16,637039,1,,e-page remote auto,,,Bank Of Jupiter,Europa,IO,,

我想计算逗号的数量,不包括引号中的逗号,例如“Allied Martian Banks, P.L.C.”。

我知道:

length(i.data_record)-length(replace(i.data_record,',',''))

将返回逗号的数量,但与第二行相比,第一行会多一个逗号,因为我的目的,它们应该被视为具有相同的数字。

有什么快速简单的方法可以忽略引号中的逗号吗?

我知道我可以创建一个循环并开始按位分解字符串,计算它们,每当我找到引号时忽略任何逗号,直到我找到另一个引号,但是我想知道是否有更简单,更多无需借助循环即可实现这一目标的简化方法。

非常感谢!

【问题讨论】:

    标签: regex string oracle plsql count


    【解决方案1】:

    先去掉分隔的内容,再计算:

    regexp_count (
        regexp_replace (
            regexp_replace (
                i.data_record
              , '(^|,)"[^"]*"(,|$)'
              , '\1\2'
            )
          , '(^|,)"[^"]*"(,|$)'
          , '\1\2'
        )
      , ',' 
    ) 
    

    不幸的是,为了正确处理连续的以引号分隔的字段,regexp_replace 调用的嵌套是必要的:任何分隔逗号都被正则表达式模式使用,因此不会被考虑到后续匹配中。

    Oracle 的正则表达式不支持前瞻运算符,这是处理这种情况的自然方法。

    鉴于 regexp_... 调用对性能的影响,您最好使用

    length(i.data_record) - length ( replace ( regexp_replace ( i.data_record, '(^|,)"[^"]*"(,|$)', '\1\2' ),',','' ) )
    

    警告

    此解决方案不处理字段值中的 dquotes,通常表示为 ""\"

    前一种情况可以很好地处理:不要在引号分隔的字段中解释"",而是将整个字段内容视为1个或多个不包含dquote的dquote分隔的字符串的并置。虽然您在处理数据时不会遵循此路线(所有 dquotes 都会丢失),但您可以使用此视角进行计数:

    regexp_count (
        regexp_replace (
            regexp_replace (
                i.data_record
              , '(^|,)("[^"]*")+(,|$)'  -- changed
              , '\1\3'                  -- changed
            )
          , '(^|,)("[^"]*")+(,|$)'   -- changed
          , '\1\3'                   -- changed
        )
      , ',' 
    ) 
    

    测试用例

    -- works
    select regexp_count ( regexp_replace ( regexp_replace ( '1,"data,and more so","more data,and even more so"', '(^|,)"[^"]*"(,|$)', '\1\2' ), '(^|,)"[^"]*"(,|$)', '\1\2' ), ',' ) from dual;
    select regexp_count ( regexp_replace ( regexp_replace ( '1,"data,and more so",2,"more data,and even more so"', '(^|,)"[^"]*"(,|$)', '\1\2' ), '(^|,)"[^"]*"(,|$)', '\1\2' ), ',' ) from dual;
    
    select regexp_count ( regexp_replace ( regexp_replace ( '1,"""data"",and more so",2,"more data,and even more so"', '(^|,)("[^"]*")+(,|$)', '\1\3' ), '(^|,)("[^"]*")+(,|$)', '\1\3' ), ',' ) from dual;
    
    -- fails
    select regexp_count ( regexp_replace ( '1,"data,and more so","more data,and even more so"', '(^|,)"[^"]*"(,|$)', '\1\2' ), ',' ) from dual;
    select regexp_count ( regexp_replace ( '1,"data,and more so",2,"more data,and even more so"', '(^|,)"[^"]*"(,|$)', '\1\2' ), ',' ) from dual;
    

    【讨论】:

      【解决方案2】:

      最好用这个

      REGEXP_COUNT(REGEXP_REPLACE(text,',".+",', ',,'), ',')

      【讨论】:

      • 不起作用,贪婪匹配会在连续的 dquote 分隔的字段之间使用字段分隔逗号。
      • Collapsar 看link 一些错误仍然发生......
      • 双引号内仍然可以有双引号:D
      • 我只是指出了你原来方法中的致命缺陷......(没有冒犯,我的第一个版本在同一个测试用例上失败了,尽管原因不太明显)。
      • 您当前的版本在以包含逗号的 dquote 分隔字段开头的记录上失败,因为您的正则表达式永远不会匹配记录 #1(前面没有逗号)。
      猜你喜欢
      • 2020-09-28
      • 1970-01-01
      • 1970-01-01
      • 2012-07-29
      • 2016-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多