【问题标题】:How to create regex pattern for arbitrary range of surrogate pairs如何为任意范围的代理对创建正则表达式模式
【发布时间】:2017-03-25 08:18:32
【问题描述】:

在“窄”Python 中构建 we should 使用特殊的正则表达式模式来匹配代理对的范围。这种模式可能相当复杂:

# Pattern we want:
pattern = '[\U000105c0-\U0001cb40]'

# Pattern we should use in "narrow" build:
pattern = '(?:\uD801[\uDDC0-\uDFFF]|[\uD802-\uD831][\uDC00-\uDFFF]|\uD832[\uDC00-\uDF40])'

但是如何为给定的任意代理范围创建一个(例如\U000105c0-\U0001cb40)?

创建这种模式的算法是什么?

在 Python 中是否有任何现成的解决方案?

【问题讨论】:

  • 您的意思是要动态替换此类模式(即使是较长的模式)?
  • @WiktorStribiżew 假设为两个给定字符生成:get_pattern('\U000105c0', '\U0001cb40')

标签: python regex unicode


【解决方案1】:

安装http://www.regexformat.com 应用程序。
(适用于窗户)

您可以在任何范围内执行以下操作。
只需要一个正则表达式来描述它(或任何东西)。

打开UCD Interfacehttp://imgur.com/S8V0mIG

Custom-Rx页面,输入[\x{105c0}-\x{1cb40}]

在输出中选择您想要的转换语法
(这使用了\x{} 语法)。

点击按钮Get Hex Conversion -> UTF-16(它是一个菜单按钮)

复制结果框底部的正则表达式。

 (?:
      \x{D801} [\x{DDC0}-\x{DFFF}] 
   |  [\x{D802}-\x{D831}] [\x{DC00}-\x{DFFF}] 
   |  \x{D832} [\x{DC00}-\x{DF40}] 
 )

如果您将其粘贴到一个主应用文档中,并且
点击压缩,结果是

(?:\x{D801}[\x{DDC0}-\x{DFFF}]|[\x{D802}-\x{D831}][\x{DC00}-\x{DFFF}]|\x{D832}[\x{DC00}-\x{DF40}])

这里使用\uXXXX语法
(?:\uD801[\uDDC0-\uDFFF]|[\uD802-\uD831][\uDC00-\uDFFF]|\uD832[\uDC00-\uDF40])

【讨论】:

    【解决方案2】:

    我创建的函数可以处理我们可能需要的大多数情况。

    Python 2 代码:

    from __future__ import absolute_import, division, print_function, unicode_literals
    __metaclass__ = type
    
    import struct
    
    
    def unichar(i):
        """
        unichr for "narrow" builds.
        """
        try:
            return unichr(i)
        except ValueError:
            return struct.pack('i', i).decode('utf-32')
    
    
    def get_pattern(char_from, char_to):
        """
        Returns regex pattern for unicode chars that handles surrogates in "narrow" builds.
        """
        if all(len(c) == 1 for c in (char_from, char_to)):
            if char_from == char_to:
                return char_from
            else:
                return '[{}-{}]'.format(char_from, char_to)
        elif all(len(c) == 2 for c in (char_from, char_to)):
            f1, f2 = [ord(i) for i in char_from]
            t1, t2 = [ord(i) for i in char_to]
            if t1 - f1 == 0:
                p1 = '{}[{}-{}]'.format(unichar(f1), unichar(f2), unichar(t2))
                return '(?:' + p1 + ')'
            elif t1 - f1 == 1:
                p1 = '{}[{}-\uDFFF]'.format(unichar(f1), unichar(f2))
                p3 = '{}[\uDC00-{}]'.format(unichar(t1), unichar(t2))
                return '(?:' + '|'.join([p1, p3]) + ')'
            else:
                p1 = '{}[{}-\uDFFF]'.format(unichar(f1), unichar(f2))
                p2 = '[{}-{}][\uDC00-\uDFFF]'.format(unichar(f1+1), unichar(t1-1), unichar(f2))
                p3 = '{}[\uDC00-{}]'.format(unichar(t1), unichar(t2))
                return '(?:' + '|'.join([p1, p2, p3]) + ')'
        else:
            raise ValueError('Range is not supported by this function {}-{}'.format(char_from, char_to))
    
    
    # Example:
    if __name__ == '__main__':
        print(repr(get_pattern('\U000105c0', '\U0001cb40')))
    
        # (?:\ud801[\uddc0-\udfff]|[\ud802-\ud831][\udc00-\udfff]|\ud832[\udc00-\udf40])
    

    【讨论】:

    • 好的,你的下一个转换任务是用你所学的做一些有用的事情。给定一个更有意义的 utf-32 类 [\u2E80-\u2EFF\u3000-\u303F\u31C0-\u31EF\u3300-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFE30-\uFE4F\U00020000-\U0002A6DF\U0002A700-\U0002CEAF\U0002F800-\U0002FA1F] 将其转换为 utf-16 (?:[\u2E80-\u2EFF\u3000-\u303F\u31C0-\u31EF\u3300-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFE30-\uFE4F]|(?:[\uD840-\uD868][\uDC00-\uDFFF]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|[\uD86A-\uD872][\uDC00-\uDFFF]|\uD873[\uDC00-\uDEAF]|\uD87E[\uDC00-\uDE1F]))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-02
    • 2012-01-10
    • 2011-06-23
    • 2019-10-04
    • 2021-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多