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?
13 réponses
NSString* finish = [[start componentsSeparatedByCharactersInSet:[[NSCharacterSet letterCharacterSet] invertedSet]] componentsJoinedByString:@""];
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.
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.
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:@""];
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.
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)
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
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.
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:
- crée un ensemble de caractères acceptés
- transformer les lettres accentuées en lettres normales
- 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
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]];
@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
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;
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"
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égoriessimplifient 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!