NSString - 仅转换为纯字母(即删除重音符号+标点符号)

26 regex string cocoa objective-c nsstring

我正在尝试比较名称,没有任何标点符号,空格,重音等.目前我正在做以下事情:

-(NSString*) prepareString:(NSString*)a {
    //remove any accents and punctuation;
    a=[[[NSString alloc] initWithData:[a dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES] encoding:NSASCIIStringEncoding] autorelease];

    a=[a stringByReplacingOccurrencesOfString:@" " withString:@""];
    a=[a stringByReplacingOccurrencesOfString:@"'" withString:@""];
    a=[a stringByReplacingOccurrencesOfString:@"`" withString:@""];
    a=[a stringByReplacingOccurrencesOfString:@"-" withString:@""];
    a=[a stringByReplacingOccurrencesOfString:@"_" withString:@""];
    a=[a lowercaseString];
    return a;
}
Run Code Online (Sandbox Code Playgroud)

但是,我需要为数百个字符串执行此操作,我需要提高效率.有任何想法吗?

Pet*_*wis 80

NSString* finish = [[start componentsSeparatedByCharactersInSet:[[NSCharacterSet letterCharacterSet] invertedSet]] componentsJoinedByString:@""];
Run Code Online (Sandbox Code Playgroud)

  • 这很性感!谢谢! (6认同)
  • 刚刚登录了`letterCharacterSet`的内容 - 它煤层含口音 - 这里是一个20字符的片断`opqrstuvwxyzªμºÀÁÂÃÄ`这里我使用的代码:https://gist.github.com/rsaunders100/6160147 (3认同)
  • 并且在`Swift`中因为`componentsJoinedByString`确实存在但不同:`let finish ="".join(start.componentsSeparatedByCharactersInSet(NSCharacterSet.letterCharacterSet().invertedSet))` (2认同)

Pet*_*sey 39

在使用任何这些解决方案之前,不要忘记使用decomposedStringWithCanonicalMapping分解任何重音字母.例如,这将把é(U + 00E9)变成e(U + 0065 U + 0301).然后,当您删除非字母数字字符时,将保留非重音字母.

这很重要的原因是你可能不希望,例如,"dän"和"dün"*被视为相同.如果你删除了所有重音字母,正如其中一些解决方案可能会做的那样,你最终会得到"dn",所以这些字符串将相等.

因此,您应该首先分解它们,以便您可以去除重音并留下字母.

*来自德国的例子.感谢Joris Weimar提供它.


Sop*_*ert 14

在类似的问题上,Ole Begemann建议使用stringByFoldingWithOptions:我相信这是最好的解决方案:

NSString *accentedString = @"ÁlgeBra";
NSString *unaccentedString = [accentedString stringByFoldingWithOptions:NSDiacriticInsensitiveSearch locale:[NSLocale currentLocale]];
Run Code Online (Sandbox Code Playgroud)

根据要转换的字符串的性质,您可能希望设置固定的区域设置(例如英语),而不是使用用户的当前区域设置.这样,您可以确保在每台机器上获得相同的结果.


小智 7

比起BillyTheKid18756的答案有一个重要的精确度(由Luiz纠正但在代码的解释中并不明显):

不要使用 stringWithCString作为删除重音的第二步,它可以在字符串的末尾添加不需要的字符,因为NSData不是以NULL结尾的(因为stringWithCString需要它).或者使用它并向NSData添加一个额外的NULL字节,就像Luiz在他的代码中所做的那样.

我认为更简单的答案是替换:

NSString *sanitizedText = [NSString stringWithCString:[sanitizedData bytes] encoding:NSASCIIStringEncoding];
Run Code Online (Sandbox Code Playgroud)

通过:

NSString *sanitizedText = [[[NSString alloc] initWithData:sanitizedData encoding:NSASCIIStringEncoding] autorelease];
Run Code Online (Sandbox Code Playgroud)

如果我收回BillyTheKid18756的代码,这里是完整正确的代码:

// The input text
NSString *text = @"BûvérÈ!@$&%^&(*^(_()-*/48";

// Defining what characters to accept
NSMutableCharacterSet *acceptedCharacters = [[NSMutableCharacterSet alloc] init];
[acceptedCharacters formUnionWithCharacterSet:[NSCharacterSet letterCharacterSet]];
[acceptedCharacters formUnionWithCharacterSet:[NSCharacterSet decimalDigitCharacterSet]];
[acceptedCharacters addCharactersInString:@" _-.!"];

// Turn accented letters into normal letters (optional)
NSData *sanitizedData = [text dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
// Corrected back-conversion from NSData to NSString
NSString *sanitizedText = [[[NSString alloc] initWithData:sanitizedData encoding:NSASCIIStringEncoding] autorelease];

// Removing unaccepted characters
NSString* output = [[sanitizedText componentsSeparatedByCharactersInSet:[acceptedCharacters invertedSet]] componentsJoinedByString:@""];
Run Code Online (Sandbox Code Playgroud)


uch*_*aka 7

如果您要比较字符串,请使用以下方法之一.不要试图改变数据.

- (NSComparisonResult)localizedCompare:(NSString *)aString
- (NSComparisonResult)localizedCaseInsensitiveCompare:(NSString *)aString
- (NSComparisonResult)compare:(NSString *)aString options:(NSStringCompareOptions)mask range:(NSRange)range locale:(id)locale
Run Code Online (Sandbox Code Playgroud)

您需要考虑用户区域设置来执行使用字符串写入的内容,尤其是名称之类的内容.在大多数语言中,ä和å等字符除了看起来相似之外并不相同.它们本质上是不同的字符,其含义与其他字符不同,但实际的规则和语义对于每个语言环境都是不同的.

比较和排序字符串的正确方法是考虑用户的语言环境.1990年的其他任何东西都是天真的,错误的.停止这样做.

如果您尝试将数据传递到不支持非ASCII的系统,那么这只是一件错误的事情.将其作为数据blob传递.

https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Strings/Articles/SearchingStrings.html

加上规范化你的琴弦(参见Peter Hosey的帖子)预先制作或分解,基本上选择一个标准化的形式.

- (NSString *)decomposedStringWithCanonicalMapping
- (NSString *)decomposedStringWithCompatibilityMapping
- (NSString *)precomposedStringWithCanonicalMapping
- (NSString *)precomposedStringWithCompatibilityMapping
Run Code Online (Sandbox Code Playgroud)

不,它不像我们想象的那么简单和容易.是的,它需要明智和谨慎的决策.(以及一些非英语语言经验有帮助)


Ale*_*lds 4

考虑使用RegexKit 框架。你可以这样做:

NSString *searchString      = @"This is neat.";
NSString *regexString       = @"[\W]";
NSString *replaceWithString = @"";
NSString *replacedString    = [searchString stringByReplacingOccurrencesOfRegex:regexString withString:replaceWithString];

NSLog (@"%@", replacedString);
//... Thisisneat
Run Code Online (Sandbox Code Playgroud)