Objective-C에서 NSString 토큰 화


답변:


274

http://borkware.com/quickies/one?topic=NSString (유용한 링크) 에서이를 발견했습니다 .

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

도움이 되었기를 바랍니다!

아담


39
장래의 독자에 대한 참고로, 나는 그 반대입니다 [anArray componentsJoinedByString:@":"];.
Ivan Vučica

2
감사하지만 더 많은 토큰으로 분리 된 NSString을 분할하는 방법은 무엇입니까? (내가 무슨 뜻인지 알면 영어 실력이 좋지 않다) @Adam
11684

2
@Adam, 나는 당신이 원하는 것이라고 생각합니다 componentsSeparatedByCharactersInSet. 아래 답변을 참조하십시오.
Wienke

32

모든 사람들이 언급 componentsSeparatedByString:했지만 자연어를 토큰 화 CFStringTokenizer하는 ( NSStringCFString공백으로 단어를 나누지 않는 중국어 / 일본어와 같이) 상호 교환도 가능합니다.


7
그리고 Mac OS X 10.6 이상에서 NSString에는 메소드가 enumerateLinesUsingBlock:있으며 enumerateSubstringsInRange:options:usingBlock:후자는 CFStringTokenizer의 블록 기반 버전입니다. developer.apple.com/mac/library/documentation/Cocoa/Reference/… : developer.apple.com/mac/library/documentation/Cocoa/Reference/… :
Peter Hosey 2013

1
enumerate방법은 iOS 4 이상에서도 사용할 수 있습니다.
bugloaf

21

문자열을 나누려면을 사용하십시오 -[NSString componentsSeparatedByString:]. 보다 복잡한 토큰 화를 위해서는 NSScanner 클래스를 사용하십시오.


7

토큰 화 요구가 더 복잡한 경우 오픈 소스 Cocoa String 토큰 화 / 파싱 툴킷 ParseKit을 확인하십시오.

http://parsekit.com

구분 문자 (예 : ':')를 사용하여 문자열을 간단하게 분할하려면 ParseKit이 과도하게 사용됩니다. 그러나 복잡한 토큰 화 요구에 대해 ParseKit은 매우 강력하고 유연합니다.

ParseKit 토큰 화 문서 도 참조하십시오 .


여전히 작동합니까? 나는 그것을 시도하고 몇 가지 오류가 발생했습니다.
griotspeak

흠? 살아 있는? ParseKit 프로젝트는 적극적으로 유지되고 있습니다. 그러나 여기 주석은 프로젝트에 버그를 신고 할 수있는 올바른 장소가 아닙니다. 버그를 제기 해야하는 경우 Google 코드와 Github에 있습니다.
Todd Ditchendorf 님 02.14. 12.

잘 들리지만 응답을 어떻게 든 편집 할 때까지 (사이트의 규칙) 다운 보트를 제거 할 수 없습니다. 아마도 작동하는 버전이나 ARC 사용 여부 등을 알 수 있습니까? 또는 당신은 어딘가에 공간을 추가 할 수 있습니다. :)
Dan Rosenstark

6

여러 문자를 토큰 화하려면 NSString을 사용할 수 있습니다 componentsSeparatedByCharactersInSet. NSCharacterSet에는 whitespaceCharacterSetand와 같은 편리한 사전 제작 세트 가 illegalCharacterSet있습니다. 그리고 유니 코드 범위의 이니셜 라이저가 있습니다.

다음과 같이 문자 세트를 결합하고이를 사용하여 토큰화할 수도 있습니다.

// 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 하나 명 이상의 멤버가 발생할 경우 1보다 작은 길이를 테스트 할 수 있습니다, 그래서 빈 문자열을 생성합니다.


공백이 모든 논리 토큰을 분리하지 않는 언어는 다루지 않습니다. 불쌍한 솔루션.
uchuugaka

@uchuugaka이 경우 토큰화할 다른 문자 집합을 사용합니다. 나는 일반적인 개념을 설명하기 위해 특정 예제를 사용하고 있습니다.
Wienke

5

"인용구"를 유지하면서 문자열을 검색어로 토큰 화하려는 경우 NSString다양한 유형의 따옴표 쌍을 존중 하는 범주는 다음과 같습니다."" '' ‘’ “”

용법:

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

1

언어 적 특징의 문자열 (단어, 단락, 문자, 문장 및 줄)을 분할하려면 문자열 열거를 사용하십시오.

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많은 비 서부 문자는 1 바이트 이상이므로 문자를 열거하는 올바른 방법 도 사용 합니다.


0

ldapsearch를 사용하여 LDAP 쿼리 후 콘솔 출력을 분할 해야하는 경우가있었습니다. 먼저 NSTask를 설정하고 실행하십시오 (여기에서 좋은 코드 샘플을 찾았습니다 : Cocoa app에서 터미널 명령 실행 ). 그러나 Ldap-query-output에서 인쇄 서버 이름 만 추출하기 위해 출력을 분할하고 구문 분석해야했습니다. 불행히도 간단한 C- 배열 연산으로 C- 문자열 / 배열을 조작한다면 전혀 문제가되지 않는 문자열 조작이 문제가되지 않습니다. 여기에 코코아 객체를 사용하는 코드가 있습니다. 더 나은 제안이 있으면 알려주세요.

//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

나는 그것이 구성 요소 많은 작업 등으로 단지 별도의 문자열로 충분하지 어디에 내 자신의 예를 건너가
가지 유형으로 토큰을 분류 1)
새로운 토큰 추가 2)
"{" "와} 사이의 모든 단어처럼 정의 폐쇄 사이의 문자열을 분리 3) "
이러한 요구 사항에 대해 Parse Kit 가 생명을 구하는 것으로 나타났습니다 .

나는 그것을 .PGN (prtable gaming notation) 파일을 매우 빠르고 정확하게 파싱하는 데 사용했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.