我应该使用Python casefold吗?

问题描述 投票:0回答:2

最近阅读了忽略大小写时的大小写和字符串比较。我读过 MSDN 标准是使用 InvariantCulture 并且绝对避免使用 toLowercase。然而,从我读到的情况来看,大小写就像是更激进的小写。我的问题是我应该在Python中使用casefold还是有更Pythonic的标准可以代替?另外,折叠盒是否通过了土耳其测试?

python python-3.x case-folding
2个回答
19
投票

1) 在Python 3中,应该使用

casefold()
来实现不区分大小写的字符串匹配。

从Python 3.0开始,字符串存储为Unicode。 Unicode 标准第 3.13 章定义了默认无大小写匹配,如下所示:

字符串 X 与字符串 Y 不区分大小写匹配当且仅当:
toCasefold(X) = toCasefold(Y)

Python 的

casefold()
实现了 Unicode 的
toCasefold()
因此,它应该用于实现不区分大小写的字符串匹配。尽管如此,仅靠折叠案例并不足以覆盖一些极端情况并通过土耳其测试(参见第 3 点)。

2) 从 Python 3.6 开始,casefold() 无法通过土耳其测试。

对于两个字符,大写 I 和点分大写 I,Unicode 标准定义了两种不同的大小写折叠映射。

默认(对于非突厥语系):
我 → 我 (U+0049 → U+0069)
� → i̇ (U+0130 → U+0069 U+0307)

替代方案(突厥语):
我 → 我 (U+0049 → U+0131)
� → 我 (U+0130 → U+0069)

Python

casefold()
只能应用默认映射,无法通过土耳其测试。例如,土耳其语单词“LıMANI”和“limanı”是无大小写的等效词,但
"LİMANI".casefold() == "limanı".casefold()
返回
False
。没有启用替代映射的选项。

3) 如何在 Python 3 中进行不区分大小写的字符串匹配。

Unicode 标准第 3.13 章描述了几种无大小写匹配算法。 规范的无盒匹配可能适合大多数用例。该算法已经考虑了所有极端情况。我们只需要添加一个选项来在非突厥语和突厥语折叠之间切换。

import unicodedata

def normalize_NFD(string):
    return unicodedata.normalize('NFD', string)

def casefold_(string, include_special_i=False):
    if include_special_i:
        string = unicodedata.normalize('NFC', string)
        string = string.replace('\u0049', '\u0131')
        string = string.replace('\u0130', '\u0069')
    return string.casefold()

def casefold_NFD(string, include_special_i=False):
    return normalize_NFD(casefold_(normalize_NFD(string), include_special_i))

def caseless_match(string1, string2, include_special_i=False):
    return  casefold_NFD(string1, include_special_i) == casefold_NFD(string2, include_special_i)

casefold_()
是 Python
casefold()
的包装器。如果其参数
include_special_i
设置为
True
,则应用突厥语映射,如果设置为
False
,则使用默认映射。

caseless_match()
string1
string2
进行规范的 casless 匹配。如果字符串是突厥语单词,
include_special_i
参数必须设置为
True

示例:

>>> caseless_match('LİMANI', 'limanı', include_special_i=True)
True
>>> caseless_match('LİMANI', 'limanı')
False
>>> caseless_match('INTENSIVE', 'intensive', include_special_i=True)
False
>>> caseless_match('INTENSIVE', 'intensive')
True

0
投票

一个老问题,但我会添加一个答案以供将来参考,并充实 Python 和 Unicode 中大小写折叠和不敏感匹配的讨论。

Unicode 定义了两组操作,第一组是大小写映射。 Unicode 定义了三种大小写:小写、大写和标题大写。这些是将文本从一种情况更改为另一种情况的字符串转换。

同样,Unicode 定义了大小写折叠操作。大小写折叠旨在在比较字符串或匹配字符串之前消除大小写区别。这与 collation 中定义的两个字符串之间的比较操作不同。

案例折叠的关键数据来源是 UCD 中的 CaseFolding.txt 文件。

为案例折叠定义了三种类型的方法:

  1. 简单的折叠盒。当您想要最小化需要使用的数据大小时,可以使用此方法。它可以在嵌入式系统中找到,并在一些正则表达式引擎中使用。它涉及将单个字符折叠为单个字符。简单的案例折叠使用带有状态
    C
    S
    的映射。
  2. 全箱折叠。这就是
    str.casefold
    使用的。单个字符可以映射到字符序列。完全折叠使用状态为
    C
    F
    的映射。
  3. 针对土耳其、阿塞拜疆、乌兹别克、鞑靼和哈萨克的突厥剪裁。这是一个可选的折叠,默认情况下不使用,但它是 Unicode 中大小写折叠的一个可用选项。

大小写折叠不是对区域设置或语言敏感的字符串操作,除了使用突厥语例外的选项。同样重要的是要注意,使用

str.casefold
的不区分大小写与
re
模块中的不区分大小写不同。

大小写折叠是其他匹配算法的构建块,包括规范无大小写匹配、兼容性无大小写匹配和标识符匹配。

正如其他答案中所指出的,Python 在折叠时不提供对土耳其语剪裁的访问。

有两种方法:

  1. 为类构建自定义函数,以在使用
    str.casefold
  2. 时处理案例折叠
  3. 利用 PyICU,它是 icu4c
  4. 的包装器

我将使用 PyICU 使用

icu.Char
icu.CaseMap
类:

def toCasefold(text:str, full:bool = True, turkic:bool = False) -> str:
    # Enumerated consonants to use with icu.CaseMap:
    # icu.U_FOLD_CASE_DEFAULT : 0
    # icu.U_FOLD_CASE_EXCLUDE_SPECIAL_I : 1
    # 
    # Enumerated consonants in icu.Char:
    # icu.Char.FOLD_CASE_DEFAULT : 0
    # icu.Char.FOLD_CASE_EXCLUDE_SPECIAL_I : 1

    option:int = 1 if turkic else 0
    if not full:
        return "".join([icu.Char.foldCase(char, option) for char in text])
    return icu.CaseMap.fold(option, text)

city = 'DİYARBAKIR'

# Default casefold of string in Python,
#
print(city.toCasefold())
# di̇yarbakir

# Full case folding in PyICU
# Could also use icu.UnicodeString.foldCase
#
print(toCasefold(city))
# di̇yarbakir

# Full case folding in PyICU, using Turkic rules in Casefolding.txt
# Could also use icu.UnicodeString.foldCase
#
print(toCasefold(city, turkic=True))
# diyarbakır

# Simple case folding in PyICU
#
print(toCasefold(city, full=False))
# dİyarbakir

# Simple case folding in PyICU, using Turkic rules in Casefolding.txt
# Could also use icu.UnicodeString.foldCase
#
print(toCasefold(city, full=False, turkic=True))
# diyarbakır

因此,

DİYARBAKIR
可以根据 Unicode 规则折叠成 di̇yarbakirdiyarbakırdıyarbakir,具体取决于案例折叠的类型和应用的选项。

© www.soinside.com 2019 - 2024. All rights reserved.