I created function that handles most of the cases we may need.
Python 2 code:
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])