NSString tokenize trong Objective-C


Câu trả lời:


274

Tìm thấy điều này tại http://borkware.com/quickies/one?topic=NSString (liên kết hữu ích):

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

Hi vọng điêu nay co ich!

Ađam


39
Để tham khảo cho các độc giả tương lai, tôi muốn lưu ý rằng điều ngược lại là [anArray componentsJoinedByString:@":"];.
Ivan Vučica

2
cảm ơn, nhưng làm cách nào để phân tách một NSString được phân tách bằng nhiều mã thông báo hơn? (Nếu bạn hiểu ý tôi, tiếng Anh của tôi không tốt lắm) @Adam
11684

2
@Adam, tôi nghĩ những gì bạn muốn là componentsSeparatedByCharactersInSet. Xem câu trả lời dưới đây.
Wienke

32

Mọi người đã đề cập componentsSeparatedByString:nhưng bạn cũng có thể sử dụng CFStringTokenizer(hãy nhớ rằng một NSStringCFStringcó thể hoán đổi cho nhau) cũng sẽ token hóa các ngôn ngữ tự nhiên (như tiếng Trung / tiếng Nhật không phân chia các từ trên khoảng trắng).


7
Và, trong Mac OS X 10.6 trở lên, NSString có các phương thức enumerateLinesUsingBlock:enumerateSubstringsInRange:options:usingBlock:, sau này là phiên bản dựa trên khối của CFStringTokenizer. developer.apple.com/mac/l
Library / documentation / Coloa / Reference / 2007

1
Các enumeratephương thức cũng có sẵn trong iOS 4 trở lên.
bugloaf

21

Nếu bạn chỉ muốn tách một chuỗi, sử dụng -[NSString componentsSeparatedByString:]. Để mã thông báo phức tạp hơn, hãy sử dụng lớp NSScanner.


7

Nếu nhu cầu mã thông báo của bạn phức tạp hơn, hãy kiểm tra bộ công cụ phân tích / phân tích mã thông báo chuỗi mã nguồn mở của tôi: ParseKit:

http://parsekit.com

Đối với việc phân tách chuỗi đơn giản bằng cách sử dụng ký tự phân cách (như ':'), ParseKit chắc chắn sẽ là quá mức cần thiết. Nhưng một lần nữa, đối với các nhu cầu mã thông báo phức tạp, ParseKit cực kỳ mạnh mẽ / linh hoạt.

Cũng xem tài liệu Mã thông báo ParseKit .


Điều này vẫn còn hoạt động? Tôi đã thử nó và có một vài lỗi tôi cố gắng tự sửa.
griotspeak

Hửm Sống sót? Dự án ParseKit được tích cực duy trì, vâng. Tuy nhiên, ý kiến ​​ở đây không phải là nơi chính xác để nộp lỗi vào dự án. Đó là trên cả Google Code và Github nếu bạn cần báo cáo lỗi.
Todd Ditchendorf

Nghe có vẻ hay, nhưng bây giờ tôi không thể xóa downvote của mình cho đến khi bạn chỉnh sửa câu trả lời bằng cách nào đó (quy tắc của trang web). Có lẽ bạn có thể lưu ý những phiên bản của những gì nó hoạt động trên, hoặc liệu nó sử dụng ARC, vv? Hoặc bạn chỉ có thể thêm một không gian ở đâu đó, tùy bạn :)
Dan Rosenstark 14/212

6

Nếu bạn muốn mã hóa trên nhiều ký tự, bạn có thể sử dụng NSString componentsSeparatedByCharactersInSet. NSCharacterSet có một số bộ được tạo sẵn tiện dụng như whitespaceCharacterSetillegalCharacterSet . Và nó có bộ khởi tạo cho phạm vi Unicode.

Bạn cũng có thể kết hợp các bộ ký tự và sử dụng chúng để token hóa, như thế này:

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

Xin lưu ý rằng componentsSeparatedByCharactersInSetsẽ tạo ra các chuỗi trống nếu nó gặp nhiều hơn một thành viên của charset liên tiếp, vì vậy bạn có thể muốn kiểm tra độ dài nhỏ hơn 1.


Không giải quyết các ngôn ngữ trong đó khoảng trắng không tách biệt tất cả các mã thông báo logic. Giải pháp kém.
uchuugaka

@uchuugaka Trong trường hợp đó, bạn sẽ sử dụng một bộ ký tự hoặc bộ ký tự khác để mã hóa. Tôi chỉ sử dụng các ví dụ cụ thể để minh họa một khái niệm chung.
Wienke

5

Nếu bạn đang tìm cách mã hóa một chuỗi thành cụm từ tìm kiếm trong khi duy trì "cụm từ được trích dẫn", thì đây là một NSStringdanh mục tôn trọng các loại cặp trích dẫn khác nhau:"" '' ‘’ “”

Sử dụng:

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

Mã số:

@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

1

Nếu bạn đang tìm cách tách tính năng ngôn ngữ của một chuỗi (Từ, đoạn, ký tự, câu và dòng), hãy sử dụng phép liệt kê chuỗi:

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 này hoạt động với các ngôn ngữ khác, nơi không gian không phải luôn luôn là dấu phân cách (ví dụ: tiếng Nhật). Ngoài ra sử dụng NSStringEnumerationByComposedCharacterSequenceslà cách thích hợp để liệt kê các ký tự, vì nhiều ký tự không phải là tây dài hơn một byte.


0

Tôi đã gặp trường hợp phải chia đầu ra giao diện điều khiển sau khi truy vấn LDAP với ldapsearch. Đầu tiên thiết lập và thực thi NSTask (Tôi đã tìm thấy một mẫu mã tốt ở đây: Thực thi lệnh đầu cuối từ ứng dụng Cacao ). Nhưng sau đó tôi phải phân tách và phân tích đầu ra để chỉ trích xuất các tên máy chủ in ra khỏi Ldap-query-output. Thật không may, đó là thao tác chuỗi khá tẻ nhạt, sẽ không có vấn đề gì nếu chúng ta thao tác chuỗi C / mảng bằng các thao tác mảng C đơn giản. Vì vậy, đây là mã của tôi sử dụng các đối tượng ca cao. Nếu bạn có đề xuất tốt hơn, cho tôi biết.

//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 /programming/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];

    }
}

0

Tôi tự tìm thấy ví dụ của mình khi không tách chuỗi theo từng thành phần, chẳng hạn như
1) Phân loại mã thông báo thành loại
2) Thêm mã thông báo mới
3) Phân tách chuỗi giữa các lần đóng tùy chỉnh như tất cả các từ giữa "{" và "} "
Đối với bất kỳ yêu cầu nào như vậy, tôi thấy Parse Kit là một trình cứu sinh.

Tôi đã sử dụng nó để phân tích các tệp .PGN (ký hiệu chơi trò chơi prtable) thành công rất nhanh và lite.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.