Python中如何实现区域感知的合法数字格式验证?
方法1:反向格式化验证(标准库实现)
核心思路:先用locale.atof尝试解析字符串,再将解析后的数字按照当前区域格式重新格式化,对比格式化结果与原字符串是否完全一致,以此过滤掉日期这类被错误解析的非法格式。
import locale locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8') def is_valid_number(s): s = s.strip() # 第一步:尝试解析数字 try: num = locale.atof(s) except ValueError: return False conv = locale.localeconv() decimal_point = conv['decimal_point'] # 区分整数和浮点数场景,处理精度匹配问题 if decimal_point in s: decimal_len = len(s.split(decimal_point)[-1]) format_str = f"%.{decimal_len}f" formatted = locale.format_string(format_str, num, grouping=True) # 去除格式化后多余的末尾零(比如原字符串是1,2,格式化后可能是1,20) formatted = formatted.rstrip('0').rstrip(decimal_point) if decimal_point in formatted else formatted else: # 整数场景按千位分隔符格式化 formatted = locale.format_string("%d", int(num), grouping=True) # 对比格式化结果与原字符串 return formatted == s # 测试用例 print(is_valid_number('1.234.567,89')) # True print(is_valid_number('31.10.2024')) # False print(is_valid_number('1234')) # True print(is_valid_number('-1.234,56')) # True print(is_valid_number('31,10,2024')) # False
方法2:区域感知正则匹配
根据当前区域的千位分隔符、小数点规则,构建精准的正则表达式,直接验证字符串格式是否符合有效数字规范。
import locale import re locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8') def build_number_regex(): conv = locale.localeconv() # 转义特殊字符避免正则语法冲突 decimal = re.escape(conv['decimal_point']) thousands = re.escape(conv['thousands_sep']) # 整数部分规则:可选负号,1-3位数字开头,后面可跟(千位分隔符+3位数字)的组合 integer_part = r"-?(\d{1,3}(?:{}?\d{{3}})*|\d+)" # 小数部分规则:可选的小数点+至少一位数字 decimal_part = r"(?:{}\d+)?" # 拼接成完整正则,要求匹配整个字符串 pattern = integer_part.format(thousands) + decimal_part.format(decimal) return re.compile(r"^{}$".format(pattern)) number_regex = build_number_regex() def is_valid_number(s): return bool(number_regex.match(s.strip())) # 测试用例 print(is_valid_number('1.234.567,89')) # True print(is_valid_number('31.10.2024')) # False print(is_valid_number('1.23.456')) # False(千位分隔符位置错误) print(is_valid_number('1234,5')) # True
方法3:第三方库pyparsing实现(可选)
如果项目允许引入第三方库,pyparsing可以更灵活地构建语法规则,实现严格的区域感知数字验证。
from pyparsing import nums, Word, Combine, Optional, Literal import locale locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8') conv = locale.localeconv() decimal_point = Literal(conv['decimal_point']) thousands_sep = Literal(conv['thousands_sep']) # 定义整数部分语法:支持千位分隔符的数字组合 integer = Combine(Word(nums, max=3) + Optional(thousands_sep + Word(nums, exact=3))[...]) # 定义小数部分语法 fraction = decimal_point + Word(nums) # 组合成完整数字语法:可选负号 + 整数 + 可选小数 number = Combine(Optional(Literal('-')) + integer + Optional(fraction)) def is_valid_number(s): try: # 要求完全匹配整个字符串 number.parse_string(s, parse_all=True) return True except: return False # 测试用例 print(is_valid_number('1.234.567,89')) # True print(is_valid_number('31.10.2024')) # False
内容的提问来源于stack exchange,提问作者Valentin Brückel
相关产品推荐
相关产品推荐

