Ios NSString localizedCompare:给定更长字符串的结果不一致
我们正在尝试使用Ios NSString localizedCompare:给定更长字符串的结果不一致,ios,cocoa,core-data,unicode,localization,Ios,Cocoa,Core Data,Unicode,Localization,我们正在尝试使用NSFetchedResultsController返回人名,并使用localizedCompare:按排序顺序填充UITableView。我们还试图在UI中提供一个节索引(每个节的第一个字符的右列)。我们为NSFetchedResultsController提供了实体上的选择器,该选择器提供了每个实体应属于的部分(特别是人名的第一个字符,大写) 在处理使用Unicode代码点的人名时,我们遇到了一个问题NSFetchedResultsController抱怨实体未按节排序 具体
NSFetchedResultsController
返回人名,并使用localizedCompare:
按排序顺序填充UITableView
。我们还试图在UI中提供一个节索引(每个节的第一个字符的右列)。我们为NSFetchedResultsController
提供了实体上的选择器,该选择器提供了每个实体应属于的部分(特别是人名的第一个字符,大写)
在处理使用Unicode代码点的人名时,我们遇到了一个问题NSFetchedResultsController
抱怨实体未按节排序
具体而言:
reason=The fetched object at index 103 has an out of order section name 'Ø. Objects must be sorted by section name'}, {
reason = "The fetched object at index 103 has an out of order section name '\U00d8. Objects must be sorted by section name'";
问题似乎在于,localizedCompare:
返回的比较值对于整个“单词”和前导字符是不同的
以下测试通过了,尽管我希望(“Ø”和“O”)与(“Østerhus”和“Osypowicz”)之间的比较结果一致
-(无效)testLocalizedSortOrder300
{
NSString*str1=@“Osowski”;
NSString*str2=@“Østerhus”;
NSString*str3=@“Osypowicz”;
NSString*字母1=@“O”;
NSString*字母2=@“Ø”;
//本地化比较:
//“奥索斯基”
nsComparisonRes=[str1本地化比较:str2];
xctasertrue(res==sensorderedscending,@“(localizedCompare:)预期“@”和“@”将被sensorderedscending,但得到%@”,str1,str2,res==sensorderedName?@“sensorderedName”:“sensorderedscending”);
//Østerhus<“Osypowicz”
res=[str2本地化比较:str3];
xctasertrue(res==sensorderedescing,@“(localizedCompare:)预期“@”和“@”将被sensorderedescing,但得到了%@”、str2、str3、res==sensorderedName?@“sensorderedName”:“sensorderedescing”);
//“O”<“Ø”
res=[字母1本地化比较:字母2];
XCTAssertTrue(res==sensorderedescending,@“(本地化比较:)预期“@”和“@”将被sensorderedescending,但得到%@”,字母1,字母2,res==sensorderedName?@“sensorderedName”:“sensorderedescending”);
}
因此,最终的问题是,给定一个使用Unicode代码点的人名(或任何其他字符串),我们如何正确地(以本地化的方式)返回与localizedCompare:
指定的排序顺序相对应的节名
此外,
localizedCompare:
在后面加上其他字符时,显然将“Ø”和“O”视为OrderedName
。我希望localizedCompare:
使用了导致这种行为的NSStringCompareOptions
标志的特定组合。
通过使用compare:options:
并启用NSDiacriticInsensitiveSearch
,您可能会得到想要的结果
为了生成节索引,最好先去掉所有扩展字符的值,然后取第一个字母。比如:
[[str1 stringByFoldingWithOptions:NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch] substringToIndex:1]
这样一来,以重音字母开头的名字,如“埃瓦德”,在你取第一个字母之前,会被转换成“爱德华”。是的,去过那里。我找到的唯一解决方案是为搜索创建第二个字段,简化字符(不要立即记住方法),并将其存储为用于搜索的第二个字段。虽然不是很优雅,但很管用 最终解决这个问题的方法是在数据库中存储规范化的节名 @马丁纳建议,让我看看这篇文章讨论了这种方法,是解决这个问题的关键时刻 虽然这并不能解释本地化比较的愚蠢行为:显然将“Ø”和“O”视为附加字符,但在我们的测试中,IMHO是一个更健壮、更完整的解决方案,适用于所有Unicode代码点 具体而言,方法是:
sectionName
)sectionName
)sectionName
)用于NSFetchedResultsController
-initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:的sectionNameKeyPath
)的
NSFetchedResultsController
的获取请求所使用的排序描述符,请确保首先按节名排序,然后按节内容的排序方式(例如人名)排序,注意使用本地化版本的比较选择器。e、 g:
[NSSortDescriptor sortDescriptorWithKey:@"sectionName" ascending:YES selector:@selector(localizedStandardCompare:)],
[NSSortDescriptor sortDescriptorWithKey:@"personName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]
NSString *decomposedString = name.decomposedStringWithCanonicalMapping;
NSRange firstCharRange = [decomposedString rangeOfComposedCharacterSequenceAtIndex:0];
NSString *firstChar = [decomposedString substringWithRange:firstCharRange];
retVal = [firstChar localizedUppercaseString];
希望这种方法对其他人来说是清晰和有用的,谢谢大家的帮助。语言环境设置是什么?我很难重现这个问题。@MartinR“en_US”这似乎是一个类似的问题,也许其中一个答案有帮助。谢谢@a-goodale。遗憾的是,我们不能使用
compare:options:
或类似选项。我们有SQLite商店作为后盾,所以我们的选择是有限的。特别是“SQLite支持的排序选择器有compare:和caseInsensitiveCompare:、localizedCompare:、localizedCaseInsensitiveCompare:、localizedCaseInsensitiveCompare:、和localizedStandardCompare:。后者是类似查找器的排序,大多数人在大多数情况下都应该使用。此外,不能使用SQLite存储对临时属性进行排序。”类似地,请参见stringBy
NSString *decomposedString = name.decomposedStringWithCanonicalMapping;
NSRange firstCharRange = [decomposedString rangeOfComposedCharacterSequenceAtIndex:0];
NSString *firstChar = [decomposedString substringWithRange:firstCharRange];
retVal = [firstChar localizedUppercaseString];