当我听说我的工作中有人使用
String.toLowerCase()
在数据库中存储不区分大小写的代码以实现可搜索性时,我有一个 史诗般的失败 时刻,思考它可能出错的方式:
哪些技术受到 Unicode 版本的影响?
我是否需要担心 Oracle 或 SQL Server(或其他供应商)更改其 unicode 版本并导致我的区域设置之一不会导致相同的小写或大写字符转换?
我该如何处理这个问题?我对确保使用数据库转换的“简单性”很感兴趣,但是当升级时,也会出现同样的问题。
您不想存储字符串的小写版本“以供搜索”!!
这完全是错误的做法。您对 Unicode 大小写的工作原理做出了不公正且不正确的假设。
这就是为什么 Unicode 为字符串定义了一个单独的东西,称为大小写折叠,与三种不同的大小写(小写、标题大写和大写)不同。
这里有十个不同的例子如果你使用小写字母而不是大写字母,你会做错误的事情:
ORIGINAL CASEFOLD LOWERCASE TITLECASE UPPERCASE
========================================================================
efficient efficient efficient Efficient EFFICIENT
flour flour flour Flour FLOUR
poſt post poſt Poſt POST
poſt post poſt Poſt POST
ſtop stop ſtop Stop STOP
tschüß tschüss tschüß Tschüß TSCHÜSS
weiß weiss weiß Weiß WEISS
WEIẞ weiss weiß Weiß WEIẞ
στιγμας στιγμασ στιγμας Στιγμας ΣΤΙΓΜΑΣ
ᾲ στο διάολο ὰι στο διάολο ᾲ στο διάολο Ὰͅ Στο Διάολο ᾺΙ ΣΤΟ ΔΙΆΟΛΟ
是的,我知道 stigma 的复数是 stigmata 而不是 stigmas;我试图展示最终的西格玛问题。 ς 和 σ 都是大写西格玛 Σ 的有效小写版本。如果你“只存储小写字母”,那么你会得到错误的结果。
如果您使用 Java 的
Pattern
类,则必须同时指定
CASE_INSENSITIVE
和
UNICODE_CASE
,但您仍然无法获得正确的结果,因为虽然 Java 使用完整的大小写映射,但它仅使用简单的大小写折叠。这是一个问题。 至于突厥语,是的,突厥语确实有一个特殊的情况。例如,
stanbul 的突厥语折页只是 ı̇stanbul,而不是您应该得到的 i̇stanbul。因为我确信这些对你来说不合适,所以我将用非 ASCII 的命名字符来拼写它;更简单地说,"\N{LATIN CAPITAL LETTER I WITH DOT ABOVE}stanbul"
的突厥语折页是
"\N{LATIN SMALL LETTER DOTLESS I}\N{COMBINING DOT ABOVE}stanbul"
,而不是您通常得到的
"i\N{COMBINING DOT ABOVE}stanbul"
。如果您正在编写回归测试套件,这里还有更多表行:
[ "Henry Ⅷ", "henry ⅷ", "henry ⅷ", "Henry Ⅷ", "HENRY Ⅷ", ],
[ "I Work At Ⓚ", "i work at ⓚ", "i work at ⓚ", "I Work At Ⓚ", "I WORK AT Ⓚ", ],
[ "ʀᴀʀᴇ", "ʀᴀʀᴇ", "ʀᴀʀᴇ", "Ʀᴀʀᴇ", "ƦᴀƦᴇ", ],
[ "Ԧԧ", "ԧԧ", "ԧԧ", "Ԧԧ", "ԦԦ", ],
[ "𐐼𐐯𐑅𐐨𐑉𐐯𐐻", "𐐼𐐯𐑅𐐨𐑉𐐯𐐻", "𐐼𐐯𐑅𐐨𐑉𐐯𐐻", "𐐔𐐯𐑅𐐨𐑉𐐯𐐻", "𐐔𐐇𐐝𐐀𐐡𐐇𐐓", ],
[ "Ὰͅ", "ὰι", "ᾲ", "Ὰͅ", "ᾺΙ", ],
每列都是 orig、fold、lc、tc 和 uc,就像我在上面的早期表格中所做的那样。再次注意最后一行的大小写与小写不同。
至于Java未来版本中可能发生的unicode变化,我认为不值得编写代码来处理这个问题。记录产品支持 Java 6,然后转向客户真正想要的功能。