不区分大小写的存储和unicode兼容性

Ste*_*hen 15 unicode compatibility

在我听到有人在我的工作中使用String.toLowerCase()在数据库中存储不区分大小写的代码以进行搜索之后,我有一个史诗般的失败时刻,想到它可能出错的方式:

  • 土耳其测试(特别是在正在运行的计算机上更改区域设置)
  • Unicode版本升级 - 我的意思是,谁知道这些东西?如果我升级到Java 7,如果我不区分大小写,我必须重新索引我的数据?

哪些技术受Unicode版本的影响?

我是否需要担心Oracle或SQL Server(或其他供应商)更改其unicode版本并导致我的某个区域设置不会导致相同的低位或高位字符转换?

我该如何管理?我被确保使用数据库转换的"简单性"所诱惑,但是当有升级时,它将成为同样的问题.

tch*_*ist 35

您不希望存储字符串的小写版本"for searchability"!!

这完全是错误的做法.您正在对Unicode外壳如何工作做出不公正和不正确的假设.

这就是为什么Unicode为字符串定义了一个名为casefold的单独的东西,不同于三种不同的情况(小写,标题和大写).

以下是十个不同的示例,如果您使用小写而不是casefold ,那么您将做错事:

ORIGINAL        CASEFOLD        LOWERCASE   TITLECASE  UPPERCASE
========================================================================
e?cient         efficient       e?cient       E?cient         EFFICIENT       
?our            flour           ?our           Flour           FLOUR           
po?t            post            po?t           Po?t            POST            
po?             post            po?             Po?            POST            
?op             stop            ?op            Stop            STOP            
tschüß          tschüss         tschüß         Tschüß         TSCHÜSS         
weiß            weiss           weiß           Weiß            WEISS           
WEI?            weiss           weiß            Weiß           WEI?            
???????         ???????         ???????         ???????         ??????? 
? ??? ??????    ?? ??? ??????   ? ??? ??????    ?? ??? ??????   ?? ??? ??????        
Run Code Online (Sandbox Code Playgroud)

是的,我知道耻辱的复数是耻辱而不是耻辱; 我试图显示最终的sigma问题.ζ和σ都是大写sigma的有效小写版本Σ.如果你存储"只是小写",那么你会得到错误的东西.

如果您使用的是Java Pattern类,则必须同时指定CASE_INSENSITIVEUNICODE_CASE,并且仍然无法正确使用它们,因为虽然Java使用完整的casemapping,但它只使用简单的casefolding.这是个问题.

至于突厥语言,是的,突厥语确实有一个特殊的案例.例如,伊斯坦布尔有突厥casefold只是伊斯坦布尔,而不是伊斯坦布尔,你应该得到的.因为我确信那些看起来不合适你,我会用非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 ?", ],
[ "????", "????", "????", "????", "????",  ],
[ "??", "??", "??", "??", "??",   ],
[ "", "", "", "", "",   ],
[ "??", "??", "?", "??", "??",  ],
Run Code Online (Sandbox Code Playgroud)

每列都是orig,fold,lc,tc和uc,就像我在上面的表格中所做的那样.再次注意最后一行的casefold与其小写的不同之处.

  • @Stephen:是的,使用casefolds.w3.org建议通常是正确的,但Java的`equalsIgnoreCase`被破坏了.它不使用Unicode casefolding表,所以误报"weiß"和"WEIẞ"不等于除了大小写,它们肯定是.它也有一个关于长度不变的愚蠢​​想法,因为它使用char上/下而不是字符串upper/lower.例如,`"weiß"`的大写字符串是"WEISS".在Unicode中,两个字符串不区分大小写相同**如果它们具有相同的casefolds.**所以我只存储casefolds,然后折叠我的搜索项. (2认同)
  • @Stephen:赢了!我刚刚验证了ICU4J的`CaseInsensitiveString.equals`在常规Java失败的情况下得到了正确的答案; 例如:``fl our and water"`&``FLOUR AND WATER"`,``effi cient"`&""EFFICIENT"`,``po ft"`&``post"`,``weiß"`` "WEIẞ"`,``tschüß"`&``TSCHÜSS"`,``ᾲστοδιάολο"`&`"ᾺΣτοΔιάολο"`和``ᾲστοδιάολο"`&`"ᾺΙΣΤΟΔΙΆΟΛΟ"`.这是因为它实际上使用了casefold. (2认同)

Ste*_*hen 0

我认为最长远的解决方案是

  • 将当前默认语言环境和技术堆栈版本(在我的例子中是 Java 版本)记录到配置中
  • 如果它发生了变化(自上次启动以来,或运行区域设置 - 取决于所述技术堆栈加载的方式),则锁定存储并重新索引所有受影响的数据集。

显然,这需要发生在主接口级别;如果我在 java 中进行这些更改,我最好希望它是我唯一的数据接口机制(例如,其他技术人员不会查询底层表存储)