You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

视觉一致的变音字符ö为何不匹配?Python DataFrame外连接问题

视觉相同的字符ö为何无法匹配?Python DataFrame外连接的字符编码问题

问题重现

在对两个包含带重音字符的DataFrame执行外连接时,出现了视觉完全一致的ö字符无法被程序识别为同一字符的问题:外连接结果中,problematic_ö.txt被分成了两行(分别属于left_only和right_only),而non-problematic_ö.txt则正常匹配。将这两个ö复制到文本编辑器搜索时,也无法同时命中。

测试数据

df1.csv内容:

filename;abstract
problematic_ö.txt;abc
non-problematic_ö.txt;yxz

df2.csv内容:

bytes;filename
374;problematic_ö.txt
128;non-problematic_ö.txt

测试代码

import csv
import pandas as pd

df1 = pd.read_csv('df1.csv', header=0, sep=';')
df2 = pd.read_csv('df2.csv', header=0, sep=';')

df_outerjoin = pd.merge(df1, df2, how='outer', indicator=True)
print(df_outerjoin)

输出结果

filename abstract   bytes      _merge
0      problematic_ö.txt      abc     NaN   left_only
1  non-problematic_ö.txt      yxz   128.0        both
2      problematic_ö.txt      NaN   374.0  right_only

原因分析

问题出在Unicode字符的两种表示形式:

  • 一种是预合成字符:单个Unicode码点U+00F6,直接表示字符ö
  • 另一种是分解字符:由基础字符o(U+006F)加上分音符¨(U+0308)组合而成

这两种表示视觉上完全一致,但底层编码完全不同,因此会被程序判定为不同字符,导致连接失败。

解决方法

1. Unicode字符归一化(推荐通用方案)

使用Python标准库unicodedata的normalize方法,将所有字符统一转换为预合成形式(NFC)或分解形式(NFD),确保相同视觉字符的编码一致:

import unicodedata
import pandas as pd
import csv

def normalize_unicode(s):
    # NFC:将字符转换为预合成形式,适合大多数场景
    return unicodedata.normalize('NFC', s)

# 读取数据后对filename列做归一化
df1 = pd.read_csv('df1.csv', sep=';')
df2 = pd.read_csv('df2.csv', sep=';')

df1['filename'] = df1['filename'].apply(normalize_unicode)
df2['filename'] = df2['filename'].apply(normalize_unicode)

# 执行外连接
df_outerjoin = pd.merge(df1, df2, how='outer', indicator=True)
print(df_outerjoin)

处理后,两种ö会被统一编码,外连接将正确匹配所有同名文件。

2. 读取时指定正确编码(针对文件编码不一致)

如果问题是由于CSV文件的编码格式不统一导致的(比如一个是UTF-8,一个是ISO-8859-1),可以在pd.read_csv时指定对应编码:

df1 = pd.read_csv('df1.csv', sep=';', encoding='utf-8')
df2 = pd.read_csv('df2.csv', sep=';', encoding='iso-8859-1')

但这种方法仅解决文件编码差异,无法处理Unicode字符的合成/分解问题。

3. 手动字符替换(仅适用于特定字符)

如果仅存在ö这一种问题字符,可以直接进行替换,但这种方法通用性差:

# 注意:这里需要确保替换的是两种不同的ö字符
df1['filename'] = df1['filename'].replace('\u006F\u0308', '\u00F6', regex=True)
df2['filename'] = df2['filename'].replace('\u006F\u0308', '\u00F6', regex=True)

内容的提问来源于stack exchange,提问作者Madamadam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 17:42:35