NSString-conversion à l'alphabet pur seulement (c.-à-d. supprimer les accents+la ponctuation)

j'essaie de comparer des noms sans aucune ponctuation, espaces, accents, etc. En ce moment je fais ce qui suit:

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

cependant, je dois le faire pour des centaines de cordes et je dois rendre cela plus efficace. Des idées?

25
demandé sur dandan78 2009-08-05 11:57:57

13 réponses

NSString* finish = [[start componentsSeparatedByCharactersInSet:[[NSCharacterSet letterCharacterSet] invertedSet]] componentsJoinedByString:@""];
77
répondu Peter N Lewis 2009-08-05 08:54:40

Avant d'utiliser l'une de ces solutions, n'oubliez pas d'utiliser decomposedStringWithCanonicalMapping à décomposer les lettres accentuées. Cela transformera, par exemple, é (U+00E9) en e (U+0065 U+0301). Ensuite, lorsque vous dépouiller les caractères non alphanumériques, les lettres non accentuées restera.

la raison pour laquelle c'est important est que vous ne voulez probablement pas, dites," dän "et" dün " * d'être traités de la même façon. Si vous avez supprimé toutes les lettres accentuées, comme certaines de ces solutions peuvent faire, vous vous retrouverez avec "dn", de sorte que ces chaînes sont considérées comme égales.

donc, vous devriez les décomposer d'abord, de sorte que vous pouvez enlever les accents et laisser les lettres.

* exemple de l'Allemand. Merci à Joris de Weimar.

39
répondu Peter Hosey 2012-05-24 00:02:43

sur une question similaire, Ole Begemann suggère d'utiliser stringByFoldingWithOptions: et je crois que c'est la meilleure solution ici:

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

en fonction de la nature des chaînes que vous voulez convertir, Vous pouvez définir une locale fixe (par exemple en anglais) au lieu d'utiliser la locale actuelle de l'utilisateur. De cette façon, vous pouvez être sûr d'obtenir les mêmes résultats sur chaque machine.

14
répondu Sophie Alpert 2017-05-23 12:18:13

une précision importante par rapport à la réponse de BillyTheKid18756 (qui a été corrigée par Luiz mais qui n'était pas évidente dans l'explication du code):

ne pas utiliser stringWithCString comme une deuxième étape pour supprimer les accents, il peut ajouter des caractères indésirables à la fin de votre chaîne de caractères que la NSData n'est pas null-terminated (comme stringWithCString l'attend). Ou utilisez - le et ajoutez un octet nul à vos données NS, comme Luiz l'a fait dans son code.

je pense qu'une réponse plus simple est de remplacer:

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

par:

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

si je reprends le code de BillyTheKid18756, voici le code correct complet:

// 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:@""];
7
répondu Frédéric Feytons 2012-07-26 10:29:16

si vous essayez de comparer des chaînes, utilisez l'une de ces méthodes. N'essayez pas de modifier les données.

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

vous devez prendre en compte la locale d'utilisateur pour faire des choses écrire avec des chaînes, en particulier des choses comme des noms. Dans la plupart des langues, les caractères comme ä et å ne sont pas les mêmes, sauf qu'ils ont l'air similaires. Il s'agit de caractères intrinsèquement distincts avec une signification distincte des autres, mais les règles et la sémantique réelles sont distinctes à chaque endroit.

la bonne façon de comparer et de trier les chaînes est en considérant la localisation de l'utilisateur. Tout le reste est naïf, faux et très années 1990. Arrête de faire ça.

si vous essayez de transmettre des données à un système qui ne peut pas supporter les non-ASCII, Eh bien, c'est juste une mauvaise chose à faire. Passez-le comme des blobs de données.

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

plus normalisation de vos cordes d'abord (voir le post de Peter Hosey) précomposer ou décomposer, essentiellement choisir une forme normalisée.

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

non, ce n'est pas aussi simple et facile que nous le pensons. Oui, il faut prendre des décisions éclairées et prudentes. (et un peu de langue non anglaise de l'expérience aide)

7
répondu uchuugaka 2013-12-10 06:34:14

envisagez d'utiliser le RegexKit framework . Vous pourriez faire quelque chose comme:

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

NSLog (@"%@", replacedString);
//... Thisisneat
4
répondu Alex Reynolds 2009-08-05 08:12:39

envisager d'utiliser NSScanner , et en particulier les méthodes -setCharactersToBeSkipped: (qui accepte un ensemble de caractères) et -scanString:intoString: (qui accepte une chaîne et renvoie la chaîne scannée par référence).

vous pouvez également vouloir coupler cela avec -[NSString localizedCompare:] , ou peut-être -[NSString compare:options:] avec le Nsdiacriticinsensitivesensitivearch option. Cela pourrait simplifier avoir à supprimer / remplacer les accents, de sorte que vous pouvez vous concentrer sur la suppression de la ponctuation, espace blanc, etc.

si vous devez utiliser une approche comme celle que vous avez présentée dans votre question, utilisez au moins une chaîne Nsmutables et replaceOccurrencesOfString:withString:options:range: - qui sera beaucoup plus efficace que de créer des tonnes de chaînes autoreleased presque identiques. Il se pourrait que le simple fait de réduire le nombre d'allocations stimule la performance "suffisamment" pour le moment.

4
répondu Quinn Taylor 2009-08-05 13:56:43

Pour donner un exemple complet en combinant les réponses de Luiz et Peter, en ajoutant quelques lignes, vous obtenez le code ci-dessous.

le code fait ce qui suit:

  1. crée un ensemble de caractères acceptés
  2. transformer les lettres accentuées en lettres normales
  3. supprimer les caractères ne figurant pas dans l'ensemble

Objectif-C

// The input text
NSString *text = @"BûvérÈ!@$&%^&(*^(_()-*/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) exemple

let text = "BûvérÈ!@$&%^&(*^(_()-*/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("")

sortie

la sortie pour les deux exemples serait: BuverE!_ -48

4
répondu Vegard 2016-04-13 11:07:20

vient de tomber sur ceci, peut-être trop tard, Mais voici ce qui a fonctionné pour moi:

// 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]];
3
répondu Luiz Scheidegger 2011-06-28 22:47:41
@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
1
répondu lorean 2012-11-19 19:27:36

ces réponses n'ont pas fonctionné comme prévu pour moi. Plus précisément, decomposedStringWithCanonicalMapping n'a pas enlevé les accents/umlauts comme je l'avais prévu.

voici une variation sur ce que j'ai utilisé qui répond au mémoire:

// 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;
1
répondu Tricky 2014-12-02 14:40:26

Peter une Solution de Swift:

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

exemple:

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"
0
répondu Babac 2016-03-27 13:28:19

je voulais filtrer tout sauf les lettres et les chiffres, donc J'ai adapté L'implémentation par Lorean d'une catégorie sur NSString pour travailler un peu différemment. Dans cet exemple, vous spécifiez une chaîne avec uniquement les caractères que vous souhaitez conserver, et tout le reste est filtré:

@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

une fois que vous avez fait vos catégories, leur utilisation est triviale, et vous pouvez les utiliser sur n'importe quel NSString:

NSString *string = someStringValueThatYouWantToFilter;

string = [string stringByKeepingOnlyLettersAndNumbers];

ou, par exemple, si vous voulez vous débarrasser de tout sauf les voyelles:

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

si vous apprenez toujours Objectif-C et que vous n'utilisez pas de catégories, je vous encourage à les essayer. Ils sont le meilleur endroit pour mettre des choses comme ceci parce qu'il donne plus de fonctionnalité à tous les objets de la classe que vous Catégorisez.

Les catégories

simplifient et encapsulent le code que vous ajoutez, le rendant facile à réutiliser sur tous vos projet. C'est une grande caractéristique de L'objectif-C!

-1
répondu ElmerCat 2015-01-10 05:23:05