Objective-C中的NSString标记

在Objective-C中标记/分割NSString的最好方法是什么?

发现这在http://borkware.com/quickies/one?topic=NSString (有用的链接):

NSString *string = @"oop:ack:bork:greeble:ponies"; NSArray *chunks = [string componentsSeparatedByString: @":"]; 

希望这可以帮助!

亚当

每个人都提到了componentsSeparatedByString:但是你也可以使用CFStringTokenizer (记住一个NSStringCFString是可以互换的),它也会标记自然语言(比如中文/日文不会在空格上分词)。

如果你只是想分割一个string,使用-[NSString componentsSeparatedByString:] 。 对于更复杂的标记,请使用NSScanner类。

如果您的标记化需求更复杂,请查看我的开源Cocoastring标记化/parsing工具包:ParseKit:

http://parsekit.com

对于使用分隔符string(比如':')简单地分割string,ParseKit肯定会矫枉过正。 但是,对于复杂的标记化需求,ParseKit非常强大/灵活。

另请参阅ParseKit Tokenization文档 。

如果你想标记多个字符,你可以使用NSString的componentsSeparatedByCharactersInSet 。 NSCharacterSet有一些方便的预制集合,如whitespaceCharacterSetillegalCharacterSet 。 它具有Unicode范围的初始值设定项。

您还可以组合字符集并使用它们来标记,如下所示:

 // Tokenize sSourceEntityName on both whitespace and punctuation. NSMutableCharacterSet *mcharsetWhitePunc = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy]; [mcharsetWhitePunc formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]]; NSArray *sarrTokenizedName = [self.sSourceEntityName componentsSeparatedByCharactersInSet:mcharsetWhitePunc]; [mcharsetWhitePunc release]; 

请注意,如果componentsSeparatedByCharactersInSet在一行中遇到多个charSet成员,将生成空string,因此您可能需要testing小于1的长度。

如果您希望在保留“引用短语”的同时将string标记为search字词,则下面是一个NSString类别,该类别尊重各种引用types: "" '' '' “”

用法:

 NSArray *terms = [@"This is my \"search phrase\" I want to split" searchTerms]; // results in: ["This", "is", "my", "search phrase", "I", "want", "to", "split"] 

码:

 @interface NSString (Search) - (NSArray *)searchTerms; @end @implementation NSString (Search) - (NSArray *)searchTerms { // Strip whitespace and setup scanner NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet]; NSString *searchString = [self stringByTrimmingCharactersInSet:whitespace]; NSScanner *scanner = [NSScanner scannerWithString:searchString]; [scanner setCharactersToBeSkipped:nil]; // we'll handle whitespace ourselves // A few types of quote pairs to check NSDictionary *quotePairs = @{@"\"": @"\"", @"'": @"'", @"\u2018": @"\u2019", @"\u201C": @"\u201D"}; // Scan NSMutableArray *results = [[NSMutableArray alloc] init]; NSString *substring = nil; while (scanner.scanLocation < searchString.length) { // Check for quote at beginning of string unichar unicharacter = [self characterAtIndex:scanner.scanLocation]; NSString *startQuote = [NSString stringWithFormat:@"%C", unicharacter]; NSString *endQuote = [quotePairs objectForKey:startQuote]; if (endQuote != nil) { // if it's a valid start quote we'll have an end quote // Scan quoted phrase into substring (skipping start & end quotes) [scanner scanString:startQuote intoString:nil]; [scanner scanUpToString:endQuote intoString:&substring]; [scanner scanString:endQuote intoString:nil]; } else { // Single word that is non-quoted [scanner scanUpToCharactersFromSet:whitespace intoString:&substring]; } // Process and add the substring to results if (substring) { substring = [substring stringByTrimmingCharactersInSet:whitespace]; if (substring.length) [results addObject:substring]; } // Skip to next word [scanner scanCharactersFromSet:whitespace intoString:nil]; } // Return non-mutable array return results.copy; } @end 

如果您正在寻找拆分string(单词,段落,字符,句子和线)的语言特征,请使用string枚举:

 NSString * string = @" \n word1! word2,%$?'/word3.word4 "; [string enumerateSubstringsInRange:NSMakeRange(0, string.length) options:NSStringEnumerationByWords usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { NSLog(@"Substring: '%@'", substring); }]; // Logs: // Substring: 'word1' // Substring: 'word2' // Substring: 'word3' // Substring: 'word4' 

这个API适用于其他语言,其中空格不一定是分隔符(例如日语)。 同样使用NSStringEnumerationByComposedCharacterSequences是枚举字符的正确方法,因为许多非西方字符长度超过一个字节。

在使用ldapsearch进行LDAP查询之后,我遇到了必须拆分控制台输出的情况。 首先设置并执行NSTask(我在这里find了一个好的代码示例: 从Cocoa应用程序执行一个terminal命令 )。 但是之后,我不得不拆分和parsing输出,以便仅从Ldap-query-output中提取打印服务器名称。 不幸的是,如果我们要用简单的C数组操作来操作Cstring/数组,那么这将是没有任何问题的繁琐的string操作。 所以这里是我的代码使用cocoa对象。 如果你有更好的build议,请告诉我。

 //as the ldap query has to be done when the user selects one of our Active Directory Domains //(an according comboBox should be populated with print-server names we discover from AD) //my code is placed in the onSelectDomain event code //the following variables are declared in the interface .h file as globals @protected NSArray* aDomains;//domain combo list array @protected NSMutableArray* aPrinters;//printer combo list array @protected NSMutableArray* aPrintServers;//print server combo list array @protected NSString* sLdapQueryCommand;//for LDAP Queries @protected NSArray* aLdapQueryArgs; @protected NSTask* tskLdapTask; @protected NSPipe* pipeLdapTask; @protected NSFileHandle* fhLdapTask; @protected NSMutableData* mdLdapTask; IBOutlet NSComboBox* comboDomain; IBOutlet NSComboBox* comboPrinter; IBOutlet NSComboBox* comboPrintServer; //end of interface globals //after collecting the print-server names they are displayed in an according drop-down comboBox //as soon as the user selects one of the print-servers, we should start a new query to find all the //print-queues on that server and display them in the comboPrinter drop-down list //to find the shares/print queues of a windows print-server you need samba and the net -S command like this: // net -S yourPrintServerName.yourBaseDomain.com -U yourLdapUser%yourLdapUserPassWord -W adm rpc share -l //which dispalays a long list of the shares - (IBAction)onSelectDomain:(id)sender { static int indexOfLastItem = 0; //unfortunately we need to compare this because we are called also if the selection did not change! if ([comboDomain indexOfSelectedItem] != indexOfLastItem && ([comboDomain indexOfSelectedItem] != 0)) { indexOfLastItem = [comboDomain indexOfSelectedItem]; //retain this index for next call //the print-servers-list has to be loaded on a per univeristy or domain basis from a file dynamically or from AN LDAP-QUERY //initialize an LDAP-Query-Task or console-command like this one with console output /* ldapsearch -LLL -s sub -D "cn=yourLdapUser,ou=yourOuWithLdapUserAccount,dc=yourDomain,dc=com" -h "yourLdapServer.com" -p 3268 -w "yourLdapUserPassWord" -b "dc=yourBaseDomainToSearchIn,dc=com" "(&(objectcategory=computer)(cn=ps*))" "dn" //our print-server names start with ps* and we want the dn as result, wich comes like this: dn: CN=PSyourPrintServerName,CN=Computers,DC=yourBaseDomainToSearchIn,DC=com */ sLdapQueryCommand = [[NSString alloc] initWithString: @"/usr/bin/ldapsearch"]; if ([[comboDomain stringValue] compare: @"firstDomain"] == NSOrderedSame) { aLdapQueryArgs = [NSArray arrayWithObjects: @"-LLL",@"-s", @"sub",@"-D", @"cn=yourLdapUser,ou=yourOuWithLdapUserAccount,dc=yourDomain,dc=com",@"-h", @"yourLdapServer.com",@"-p",@"3268",@"-w",@"yourLdapUserPassWord",@"-b",@"dc=yourFirstDomainToSearchIn,dc=com",@"(&(objectcategory=computer)(cn=ps*))",@"dn",nil]; } else { aLdapQueryArgs = [NSArray arrayWithObjects: @"-LLL",@"-s", @"sub",@"-D", @"cn=yourLdapUser,ou=yourOuWithLdapUserAccount,dc=yourDomain,dc=com",@"-h", @"yourLdapServer.com",@"-p",@"3268",@"-w",@"yourLdapUserPassWord",@"-b",@"dc=yourSecondDomainToSearchIn,dc=com",@"(&(objectcategory=computer)(cn=ps*))",@"dn",nil]; } //prepare and execute ldap-query task tskLdapTask = [[NSTask alloc] init]; pipeLdapTask = [[NSPipe alloc] init];//instead of [NSPipe pipe] [tskLdapTask setStandardOutput: pipeLdapTask];//hope to get the tasks output in this file/pipe //The magic line that keeps your log where it belongs, has to do with NSLog (see https://stackoverflow.com/questions/412562/execute-a-terminal-command-from-a-cocoa-app and here http://www.cocoadev.com/index.pl?NSTask ) [tskLdapTask setStandardInput:[NSPipe pipe]]; //fhLdapTask = [[NSFileHandle alloc] init];//would be redundand here, next line seems to do the trick also fhLdapTask = [pipeLdapTask fileHandleForReading]; mdLdapTask = [NSMutableData dataWithCapacity:512];//prepare capturing the pipe buffer which is flushed on read and can overflow, start with 512 Bytes but it is mutable, so grows dynamically later [tskLdapTask setLaunchPath: sLdapQueryCommand]; [tskLdapTask setArguments: aLdapQueryArgs]; #ifdef bDoDebug NSLog (@"sLdapQueryCommand: %@\n", sLdapQueryCommand); NSLog (@"aLdapQueryArgs: %@\n", aLdapQueryArgs ); NSLog (@"tskLdapTask: %@\n", [tskLdapTask arguments]); #endif [tskLdapTask launch]; while ([tskLdapTask isRunning]) { [mdLdapTask appendData: [fhLdapTask readDataToEndOfFile]]; } [tskLdapTask waitUntilExit];//might be redundant here. [mdLdapTask appendData: [fhLdapTask readDataToEndOfFile]];//add another read for safety after process/command stops NSString* sLdapOutput = [[NSString alloc] initWithData: mdLdapTask encoding: NSUTF8StringEncoding];//convert output to something readable, as NSData and NSMutableData are mere byte buffers #ifdef bDoDebug NSLog(@"LdapQueryOutput: %@\n", sLdapOutput); #endif //Ok now we have the printservers from Active Directory, lets parse the output and show the list to the user in its combo box //output is formatted as this, one printserver per line //dn: CN=PSyourPrintServer,OU=Computers,DC=yourBaseDomainToSearchIn,DC=com //so we have to search for "dn: CN=" to retrieve each printserver's name //unfortunately splitting this up will give us a first line containing only "" empty string, which we can replace with the word "choose" //appearing as first entry in the comboBox aPrintServers = (NSMutableArray*)[sLdapOutput componentsSeparatedByString:@"dn: CN="];//split output into single lines and store it in the NSMutableArray aPrintServers #ifdef bDoDebug NSLog(@"aPrintServers: %@\n", aPrintServers); #endif if ([[aPrintServers objectAtIndex: 0 ] compare: @"" options: NSLiteralSearch] == NSOrderedSame){ [aPrintServers replaceObjectAtIndex: 0 withObject: slChoose];//replace with localized string "choose" #ifdef bDoDebug NSLog(@"aPrintServers: %@\n", aPrintServers); #endif } //Now comes the tedious part to extract only the print-server-names from the single lines NSRange r; NSString* sTemp; for (int i = 1; i < [aPrintServers count]; i++) {//skip first line with "choose". To get rid of the rest of the line, we must isolate/preserve the print server's name to the delimiting comma and remove all the remaining characters sTemp = [aPrintServers objectAtIndex: i]; sTemp = [sTemp stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];//remove newlines and line feeds #ifdef bDoDebug NSLog(@"sTemp: %@\n", sTemp); #endif r = [sTemp rangeOfString: @","];//now find first comma to remove the whole rest of the line //r.length = [sTemp lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; r.length = [sTemp length] - r.location;//calculate number of chars between first comma found and lenght of string #ifdef bDoDebug NSLog(@"range: %i, %i\n", r.location, r.length); #endif sTemp = [sTemp stringByReplacingCharactersInRange:r withString: @"" ];//remove rest of line #ifdef bDoDebug NSLog(@"sTemp after replace: %@\n", sTemp); #endif [aPrintServers replaceObjectAtIndex: i withObject: sTemp];//put back string into array for display in comboBox #ifdef bDoDebug NSLog(@"aPrintServer: %@\n", [aPrintServers objectAtIndex: i]); #endif } [comboPrintServer removeAllItems];//reset combo box [comboPrintServer addItemsWithObjectValues:aPrintServers]; [comboPrintServer setNumberOfVisibleItems:aPrintServers.count]; [comboPrintServer selectItemAtIndex:0]; #ifdef bDoDebug NSLog(@"comboPrintServer reloaded with new values."); #endif //release memory we used for LdapTask [sLdapQueryCommand release]; [aLdapQueryArgs release]; [sLdapOutput release]; [fhLdapTask release]; [pipeLdapTask release]; // [tskLdapTask release];//strangely can not be explicitely released, might be autorelease anyway // [mdLdapTask release];//strangely can not be explicitely released, might be autorelease anyway [sTemp release]; } } 

我有我自己遇到的情况,这是不够的,只是单独的string由组件许多任务,如
1)将令牌分类为types
2)添加新的令牌
3)在自定义closures之间分隔string,如“{”和“}”之间的所有单词
对于任何这样的要求,我发现parsing套件一个生命的救星。

我用它来parsing.PGN(prtable游戏符号)文件成功的非常快速和精简。