Подтвердить что ты не робот

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;
}

Однако мне нужно сделать это для сотен строк, и мне нужно сделать это более эффективным. Любые идеи?

4b9b3361

Ответ 1

NSString* finish = [[start componentsSeparatedByCharactersInSet:[[NSCharacterSet letterCharacterSet] invertedSet]] componentsJoinedByString:@""];

Ответ 2

Прежде чем использовать какое-либо из этих решений, не забудьте использовать decomposedStringWithCanonicalMapping для разложения любых акцентированных букв. Это превратит, например, é (U + 00E9) в e (U + 0065 U + 0301). Затем, когда вы удаляете не буквенно-цифровые символы, буквы без изменений остаются.

Причина, по которой это важно, заключается в том, что вы, вероятно, не хотите, скажем, "dän" и "dün" * рассматриваться как одно и то же. Если вы удалите все буквы с акцентом, как это может сделать некоторые из этих решений, вы получите "dn", поэтому эти строки будут сравниваться как равные.

Итак, сначала вы должны разложить их, чтобы вы могли разделить акценты и оставить буквы.

* Пример от немецкого. Благодаря Джорису Веймару за его предоставление.

Ответ 3

По аналогичному вопросу Оле Бегеманн предлагает использовать stringByFoldingWithOptions:, и я считаю, что это лучшее решение здесь:

NSString *accentedString = @"ÁlgeBra";
NSString *unaccentedString = [accentedString stringByFoldingWithOptions:NSDiacriticInsensitiveSearch locale:[NSLocale currentLocale]];

В зависимости от характера строк, которые вы хотите преобразовать, вы можете установить фиксированный языковой стандарт (например, английский) вместо использования текущего языкового стандарта пользователя. Таким образом, вы можете получить одинаковые результаты на каждой машине.

Ответ 4

Если вы пытаетесь сравнить строки, используйте один из этих методов. Не пытайтесь изменять данные.

- (NSComparisonResult)localizedCompare:(NSString *)aString
- (NSComparisonResult)localizedCaseInsensitiveCompare:(NSString *)aString
- (NSComparisonResult)compare:(NSString *)aString options:(NSStringCompareOptions)mask range:(NSRange)range locale:(id)locale

Вам НЕОБХОДИМО рассматривать пользовательский язык, чтобы делать что-то писать со строками, особенно такими, как имена. На большинстве языков такие символы, как ä и å, не отличаются от аналогичных. Они являются неотъемлемо отличными символами со значением, отличным от других, но фактические правила и семантика различны для каждой локали.

Правильный способ сравнения и сортировки строк - это изучение пользовательской локали. Все остальное наивно, неправильно и очень 1990-х. Прекратите делать это.

Если вы пытаетесь передать данные в систему, которая не может поддерживать не-ASCII, это просто неправильно. Передайте его как кадры данных.

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

Плюс нормализуя ваши строки сначала (см. сообщение Peter Hosey), предварительно компонуя или разлагая, в основном выбирайте нормализованную форму.

- (NSString *)decomposedStringWithCanonicalMapping
- (NSString *)decomposedStringWithCompatibilityMapping
- (NSString *)precomposedStringWithCanonicalMapping
- (NSString *)precomposedStringWithCompatibilityMapping

Нет, это не так просто и легко, как мы склонны думать. Да, это требует осознанного и тщательного принятия решений. (и помогает немного опыта, не связанного с английским языком).

Ответ 5

Одна важная точность по сравнению с ответом BillyTheKid18756 (который был исправлен Луизом, но это не было очевидно при объяснении кода):

НЕ ИСПОЛЬЗУЙТЕ stringWithCString в качестве второго шага для удаления акцентов, он может добавить нежелательные символы в конце строки, поскольку NSData не завершен NULL (как ожидает stringWithCString). Или используйте его и добавьте дополнительный NULL-байт в NSData, как это сделал Луис в своем коде.

Я думаю, что более простой ответ заключается в замене:

NSString *sanitizedText = [NSString stringWithCString:[sanitizedData bytes] encoding:NSASCIIStringEncoding];

По:

NSString *sanitizedText = [[[NSString alloc] initWithData:sanitizedData encoding:NSASCIIStringEncoding] autorelease];

Если я верну код BillyTheKid18756, вот полный правильный код:

// The input text
NSString *text = @"BûvérÈ[email protected]$&%^&(*^(_()-*/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:@""];

Ответ 6

Рассмотрим использование Framework RegexKit. Вы можете сделать что-то вроде:

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

NSLog (@"%@", replacedString);
//... Thisisneat

Ответ 7

Рассмотрите возможность использования NSScanner и, в частности, методов -setCharactersToBeSkipped: (который принимает NSCharacterSet) и -scanString:intoString: (который принимает строку и возвращает отсканированную строку по ссылке).

Вы также можете связать это с -[NSString localizedCompare:], или, возможно, -[NSString compare:options:] с опцией NSDiacriticInsensitiveSearch. Это может упростить удаление/замену акцентов, поэтому вы можете сосредоточиться на удалении пропусков, пробелов и т.д.

Если вы должны использовать такой подход, как вы представили в своем вопросе, по крайней мере, используйте NSMutableString и replaceOccurrencesOfString:withString:options:range: - это будет намного эффективнее, чем создание тонны почти идентичных строк с автореализацией. Может быть, просто сокращение количества распределений покажет производительность "достаточно".

Ответ 8

Чтобы дать полный пример, объединив ответы Луиса и Питера, добавив несколько строк, вы получите код ниже.

Код выполняет следующие действия:

  • Создает набор принятых символов
  • Поверните акцентированные буквы в обычные буквы
  • Удалить символы не в наборе

Objective-C

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

// Create set of accepted characters
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];
NSString *sanitizedText = [NSString stringWithCString:[sanitizedData bytes] encoding:NSASCIIStringEncoding];

// Remove characters not in the set
NSString* output = [[sanitizedText componentsSeparatedByCharactersInSet:[acceptedCharacters invertedSet]] componentsJoinedByString:@""];

Пример Swift (2.2)

let text = "BûvérÈ[email protected]$&%^&(*^(_()-*/48"

// Create set of accepted characters
let acceptedCharacters = NSMutableCharacterSet()
acceptedCharacters.formUnionWithCharacterSet(NSCharacterSet.letterCharacterSet())
acceptedCharacters.formUnionWithCharacterSet(NSCharacterSet.decimalDigitCharacterSet())
acceptedCharacters.addCharactersInString(" _-.!")

// Turn accented letters into normal letters (optional)
let sanitizedData = text.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)
let sanitizedText = String(data: sanitizedData!, encoding: NSASCIIStringEncoding)

// Remove characters not in the set
let components = sanitizedText!.componentsSeparatedByCharactersInSet(acceptedCharacters.invertedSet)
let output = components.joinWithSeparator("")

Выход

Вывод для обоих примеров будет следующим: BuverE! _-48

Ответ 9

Просто наткнулся на это, может быть, слишком поздно, но вот что сработало для меня:

// text is the input string, and this just removes accents from the letters

// lossy encoding turns accented letters into normal letters
NSMutableData *sanitizedData = [text dataUsingEncoding:NSASCIIStringEncoding
                                  allowLossyConversion:YES];

// increase length by 1 adds a 0 byte (increaseLengthBy 
// guarantees to fill the new space with 0s), effectively turning 
// sanitizedData into a c-string
[sanitizedData increaseLengthBy:1];

// now we just create a string with the c-string in sanitizedData
NSString *final = [NSString stringWithCString:[sanitizedData bytes]];

Ответ 10

@interface NSString (Filtering)
    - (NSString*)stringByFilteringCharacters:(NSCharacterSet*)charSet;
@end

@implementation NSString (Filtering)
    - (NSString*)stringByFilteringCharacters:(NSCharacterSet*)charSet {
      NSMutableString * mutString = [NSMutableString stringWithCapacity:[self length]];
      for (int i = 0; i < [self length]; i++){
        char c = [self characterAtIndex:i];
        if(![charSet characterIsMember:c]) [mutString appendFormat:@"%c", c];
      }
      return [NSString stringWithString:mutString];
    }
@end

Ответ 11

Эти ответы не работали так, как ожидалось для меня. В частности, decomposedStringWithCanonicalMapping не разделил акценты/умлауты, как я ожидал.

Здесь вариация того, что я использовал, отвечает на краткое:

// replace accents, umlauts etc with equivalent letter i.e 'é' becomes 'e'.
// Always use en_GB (or a locale without the characters you wish to strip) as locale, no matter which language we're taking as input
NSString *processedString = [string stringByFoldingWithOptions: NSDiacriticInsensitiveSearch locale: [NSLocale localeWithLocaleIdentifier: @"en_GB"]];
// remove non-letters
processedString = [[processedString componentsSeparatedByCharactersInSet:[[NSCharacterSet letterCharacterSet] invertedSet]] componentsJoinedByString:@""];
// trim whitespace
processedString = [processedString stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
return processedString;

Ответ 12

Решение Peter в Swift:

let newString = oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.letterCharacterSet().invertedSet).joinWithSeparator("")

Пример:

let oldString = "Jo_ - h !. nn y"
// "Jo_ - h !. nn y"
oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.letterCharacterSet().invertedSet)
// ["Jo", "h", "nn", "y"]
oldString.componentsSeparatedByCharactersInSet(NSCharacterSet.letterCharacterSet().invertedSet).joinWithSeparator("")
// "Johnny"

Ответ 13

Я хотел отфильтровать все, кроме букв и цифр, поэтому я адаптировал Lorean для реализации категории на NSString, чтобы работать немного иначе. В этом примере вы указываете строку с только символами, которые хотите сохранить, и все остальное отфильтровано:

@interface NSString (PraxCategories)
+ (NSString *)lettersAndNumbers;
- (NSString*)stringByKeepingOnlyLettersAndNumbers;
- (NSString*)stringByKeepingOnlyCharactersInString:(NSString *)string;
@end


@implementation NSString (PraxCategories)

+ (NSString *)lettersAndNumbers { return @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; }

- (NSString*)stringByKeepingOnlyLettersAndNumbers {
    return [self stringByKeepingOnlyCharactersInString:[NSString lettersAndNumbers]];
}

- (NSString*)stringByKeepingOnlyCharactersInString:(NSString *)string {
    NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:string];
    NSMutableString * mutableString = @"".mutableCopy;
    for (int i = 0; i < [self length]; i++){
        char character = [self characterAtIndex:i];
        if([characterSet characterIsMember:character]) [mutableString appendFormat:@"%c", character];
    }
    return mutableString.copy;
}

@end

После того, как вы создали свои категории, использование их тривиально, и вы можете использовать их в любом NSString:

NSString *string = someStringValueThatYouWantToFilter;

string = [string stringByKeepingOnlyLettersAndNumbers];

Или, например, если вы хотите избавиться от всего, кроме гласных:

string = [string stringByKeepingOnlyCharactersInString:@"aeiouAEIOU"];

Если вы все еще учитесь Objective-C и не используете Категории, я рекомендую вам попробовать их. Они - лучшее место для таких вещей, потому что это дает больше функциональности всем объектам класса, который вы классифицируете.

Категории упрощают и инкапсулируют код, который вы добавляете, что упрощает повторное использование во всех ваших проектах. Это отличная функция Objective-C!