应使用正则提取数字结构再标准化分隔符:先用 r'[-+]?\d{1,3}(?:[.,]\d{3})*(?:[.,]\d+)?'捕获有效数字,再依位置判断并替换小数点/逗号,最后转float。

遇到 $1,234.56 或 ¥1.234,56 怎么统一转成 float
不同地区货币格式差异大,直接用 float() 会报 ValueError: could not convert string to float。核心是先剥离非数字字符(但要保留小数点和负号),再处理千分位逗号和小数点位置冲突——比如欧洲习惯用逗号作小数点、点作千分位,而美国相反。
实操建议:
- 别用
.replace(',', '')粗暴去掉所有逗号,会误杀欧洲格式里的小数点 - 优先识别货币符号前缀(如
$、¥、€)和结尾单位(如USD),用正则re.sub(r'^[^\d\-]+|[^\d\-]+$', '', s)去头去尾 - 再判断字符串里逗号和点的数量与位置:若只有一个逗号且在倒数第3位(如
1,234.56),大概率是千分位;若只有一个点且在倒数第3位(如1.234,56),大概率是欧洲小数点
用正则一步提取有效数字部分(含负号和小数)
最稳的方式不是“清洗”,而是“提取”:用 re.search() 直接捕获带符号、小数、可选千分位的数字结构,忽略周围所有干扰字符。
推荐正则模式:r'[-+]?\d{1,3}(?:[.,]\d{3})*(?:[.,]\d+)?'
说明:
-
[-+]?匹配可选正负号 -
\d{1,3}匹配整数部分开头(1~3位) -
(?:[.,]\d{3})*匹配零或多个“点或逗号+三位数字”(千分位组) -
(?:[.,]\d+)?匹配可选的小数部分(一个小数点或逗号后跟至少一位数字)
示例:
import re
def extract_number(s):
match = re.search(r'[-+]?\d{1,3}(?:[.,]\d{3})*(?:[.,]\d+)?', s)
if match:
raw = match.group()
# 统一替换小数分隔符为点,删千分位分隔符
if ',' in raw and raw.rfind(',') > raw.rfind('.'):
raw = raw.replace('.', '').replace(',', '.')
else:
raw = raw.replace(',', '')
return float(raw)
return float('nan')
批量处理 Pandas DataFrame 的货币列
对 pd.Series 用 .str.extract() 比 .apply() + 正则快得多,且自动处理空值和异常。
实操建议:
- 用
df['price'].str.extract(r'([-+]?\d{1,3}(?:[.,]\d{3})*(?:[.,]\d+)?)')得到只含数字字符串的 Series - 再链式调用
.str.replace(r'[.,](?=\d{3}($|[^.,]))', '', regex=True).str.replace(',', '.').astype(float)—— 这个正则只删千分位分隔符,保留末尾小数点 - 如果数据里混有纯整数(如
$100)和带小数($100.99),确保最终astype(float)不报错,可加errors='coerce'
为什么不用 locale.atof()?
locale.atof() 看起来省事,但它依赖系统 locale 设置,且对混合格式(如 ¥1.234,56)几乎无解——它只认当前 locale 定义的千分位/小数符号,无法自适应多币种混杂场景。
更麻烦的是:Windows 和 Linux 默认 locale 不同,同一段代码在不同机器上可能解析出错;Pandas 的 to_numeric(..., errors='coerce') 对带符号字符串也直接失败,不尝试剥离。
所以真实项目里,宁可用明确的正则提取+规则替换,也不赌 locale 配置。
真正容易被忽略的是千分位和小数点共存时的优先级判断——必须看位置,不能只看数量。比如 12,34.56(非法但可能出现)和 12.345,67(合法德语格式)需要不同策略,这时候硬编码规则不如先做一次格式探测(统计逗号/点位置分布)再分组处理。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











