【问题标题】:Regex Price Matching正则表达式价格匹配
【发布时间】:2014-02-25 15:07:25
【问题描述】:

我有一个抓取价格的网络爬虫,因为我需要它来查找字符串中的以下价格:

  • 762,50
  • 1.843,75

在我的第一个天真的实现中,我没有采用 .考虑并将第一个数字与此正则表达式完美匹配:

re.findall("\d+,\d+", string)[0]

现在我需要匹配这两种情况,我最初的想法是这样的:

re.findall("(\d+.\d+,\d+|\d+,\d+)", string)[0]

有一个想法,使用 or 运算符,可以找到第一个或第二个,这不起作用,有什么建议吗?

【问题讨论】:

  • 不应该 1.843,751,843.75 吗?
  • 不在丹麦 :) 它相当于一千八百棵丹麦克朗和七十五欧尔(相当于美分)
  • 如果您尝试使用正则表达式解析 HTML,请参阅 stackoverflow.com/questions/1732348 :)

标签: python regex python-2.7


【解决方案1】:

不需要使用或,只需将第一部分作为可选参数添加:

(?:\d+\.)?\d+,\d+

(?:\d+\.) 之后的? 使其成为可选参数。 '?:' 表示不捕获该组,只需匹配即可。

>>> re.findall(r'(?:\d+\.)?\d+,\d+', '1.843,75 762,50')
['1.843,75', '762,50']

另请注意,您必须转义与除换行符以外的任何字符匹配的.(点)(请参阅http://docs.python.org/2/library/re.html#regular-expression-syntax

【讨论】:

  • 您应该注意,如果有一个组,re.findall 不包含未被组捕获的部分。例如,re.findall(r'(\d+\.)?\d+,\d+', '1.843,75') 返回['1.']
  • 发现添加了括号,使正确的结果出现在点 [0],像这样 ((\d+\.)?\d+,\d+)。我更喜欢哪个,但很好的解决方案谢谢!
  • @mrhn,使用非捕获组(?:...),您不需要用括号将整个模式括起来。
  • 我用第一组的非捕获括号更新了我的答案,这样你就只有价格列表,而不是两个组的元组,正如@falsetru 提到的那样
【解决方案2】:

在正则表达式中,点 (.) 匹配任何字符(换行符除外,除非未设置 DOTALL 标志)。转义它以匹配 . 字面意思:

\d+\.\d+,\d+|\d+,\d+
   ^^

要匹配多个前导数字,正则表达式应为:

>>> re.findall(r'(?:\d+\.)*\d+,\d+', '1,23 1.843,75   123.456.762,50')
['1,23', '1.843,75', '123.456.762,50']

注意使用非捕获组,因为 re.findall 如果模式中存在一个或多个组,则返回组列表。

更新

>>> re.findall(r'(?<![\d.])\d{1,3}(?:\.\d{3})*,\d+',
...            '1,23 1.843,75   123.456.762,50  1.2.3.4.5.6.789,123')
['1,23', '1.843,75', '123.456.762,50']

【讨论】:

  • @C.B.,感谢您的评论。顺便说一句,\.? 不适用于1,75
  • @C.B.,我根据您的评论更新了答案。
  • 这将匹配1.2.3.4.5.6.789,123
【解决方案3】:

怎么样:

(\d+[,.]\d+(?:[.,]\d+)?)

匹配:

- 一些数字后跟 , 或 .还有一些数字

- 一些数字后跟 , 或 .和一些数字后跟 , 或 .还有一些数字

匹配:762,50 和 1.843,75 和 1,75

它也会匹配 1.843.75 你同意吗?

See it in action.

【讨论】:

    【解决方案4】:

    我会用这个:

    \d{1,3}(?:\.\d{3})*,\d\d
    

    这将匹配以点为千位分隔符的数字

    【讨论】:

      【解决方案5】:
      \d*\.?\d{3},\d{2}
      

      See the working example here

      【讨论】:

        【解决方案6】:

        这可能比正则表达式慢,但考虑到您正在解析的字符串可能很短,这应该没关系。

        由于下面的解决方案不使用正则表达式,因此它更简单,并且您可以更加确定您找到了有效的浮点数。此外,它将数字字符串解析为 Python 浮点数,这可能是您打算执行的下一步。

        import locale
        locale.setlocale(locale.LC_ALL, 'en_DK.UTF-8')
        
        def float_filter(iterable):
            result = []
            for item in iterable:
                try:
                    result.append(locale.atof(item))
                except ValueError:
                    pass
            return result
        
        text = 'The price is 762,50 kroner'
        
        print(float_filter(text.split()))
        

        产量

        [762.5]
        

        基本思想:通过设置丹麦语言环境,locale.atof 将逗号解析为十进制标记,将点解析为分组分隔符。

        In [107]: import locale
        
        In [108]: locale.setlocale(locale.LC_ALL, 'en_DK.UTF-8')
        Out[108]: 'en_DK.UTF-8'
        
        In [109]: locale.atof('762,50')
        Out[109]: 762.5
        
        In [110]: locale.atof('1.843,75')
        Out[110]: 1843.75
        

        【讨论】:

          【解决方案7】:

          通常,您有一组零个或多个XXX.,后跟一个或多个XXX,,每个最多3 个数字,后跟两个数字(总是)。您是否还想支持1,375 之类的数字(没有'cents'?)。还需要避免一些误检测的情况。

          看起来像这样:

          matcher=r'((?:(?:(?:\d{1,3}\.)?(?:\d{3}.)*\d{3}\,)|(?:(?<![.0-9])\d{1,3},))\d\d)'
          
          re.findall(matcher, '1.843,75     762,50')
          

          这会检测到很多边界情况,但可能无法捕捉到所有情况......

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-04-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多