2013-04-02 6 views
9

The ProblemObetnij łańcuch zawierający emoji lub znaki unicode na słowo lub znak granice

Jak mogę obciąć ciąg przy danej długości bez unicestwienia znak Unicode, które mogą być w samym środku mojego długości? W jaki sposób można określić indeks początku znaku Unicode w ciągu znaków, aby uniknąć tworzenia brzydkich łańcuchów. Kwadrat z połową widocznego A to lokalizacja innej obciętej postaci emoji.

-(NSMutableAttributedString*)constructStatusAttributedStringWithRange:(CFRange)range 

NSString *original = [_postDictionay objectForKey:@"message"]; 

NSMutableString *truncated = [NSMutableString string]; 

NSArray *components = [original componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 

for(int x=0; x<[components count]; x++) 
{ 
    //If the truncated string is still shorter then the range desired. (leave space for ...) 
    if([truncated length]+[[components objectAtIndex:x] length]<range.length-3) 
    { 
     //Just checking if its the first word 
     if([truncated length]==0 && x==0) 
     { 
      //start off the string 
      [truncated appendString:[components objectAtIndex:0]]; 
     } 
     else 
     { 
      //append a new word to the string 
      [truncated appendFormat:@" %@",[components objectAtIndex:x]]; 
     } 

    } 
    else 
    { 
     x=[components count]; 
    } 
} 

if([truncated length]==0 || [truncated length]< range.length-20) 
{ 
    truncated = [NSMutableString stringWithString:[original substringWithRange:NSMakeRange(range.location, range.length-3)]]; 
} 

[truncated appendString:@"..."]; 

NSMutableAttributedString *statusString = [[NSMutableAttributedString alloc]initWithString:truncated]; 
[statusString addAttribute:(id)kCTFontAttributeName value:[StyleSingleton streamStatusFont] range:NSMakeRange(0, [statusString length])]; 
[statusString addAttribute:(id)kCTForegroundColorAttributeName value:(id)[StyleSingleton streamStatusColor].CGColor range:NSMakeRange(0, [statusString length])]; 

return statusString; 

} 

UPDATE Dzięki odpowiedź, był w stanie wykorzystać jedną prostą funkcję do moich potrzeb!

-(NSMutableAttributedString*)constructStatusAttributedStringWithRange:(CFRange)range 
{ 
NSString *original = [_postDictionay objectForKey:@"message"]; 

NSMutableString *truncated = [NSMutableString stringWithString:[original substringWithRange:[original rangeOfComposedCharacterSequencesForRange:NSMakeRange(range.location, range.length-3)]]]; 
[truncated appendString:@"..."]; 

NSMutableAttributedString *statusString = [[NSMutableAttributedString alloc]initWithString:truncated]; 
[statusString addAttribute:(id)kCTFontAttributeName value:[StyleSingleton streamStatusFont] range:NSMakeRange(0, [statusString length])]; 
[statusString addAttribute:(id)kCTForegroundColorAttributeName value:(id)[StyleSingleton streamStatusColor].CGColor range:NSMakeRange(0, [statusString length])]; 

return statusString; 

} 

Odpowiedz

14

NSString ma metodę rangeOfComposedCharacterSequencesForRange że możesz użyć, aby znaleźć zakres obejmujący w ciągu zawierającym tylko pełne znaki. Na przykład

NSString *s = @""; 
NSRange r = [s rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, 1)]; 

daje zakres { 0, 2 } ponieważ Emotikon jest przechowywana w postaci dwóch znaków UTF-16 (surogat para) w ciąg.

Uwagi: Można również sprawdzić, czy można uprościć pierwszą pętlę przy użyciu

enumerateSubstringsInRange:options:usingBlock 

z opcją NSStringEnumerationByWords.

+0

Dziękuję Martin! –

2

„obciąć ciąg przy danej długości” < - Ma pan na myśli długość jak długość bajtów lub długości jak w liczbie znaków? Jeśli to drugie, wystarczy prosty substringToIndex: (najpierw sprawdź granice). Jeśli były, to obawiam się, że będziesz musiał zrobić coś takiego:

NSString *TruncateString(NSString *original, NSUInteger maxBytesToRead, NSStringEncoding targetEncoding) { 
    NSMutableString *truncatedString = [NSMutableString string]; 

    NSUInteger bytesRead = 0; 
    NSUInteger charIdx = 0; 

    while (bytesRead < maxBytesToRead && charIdx < [original length]) { 
     NSString *character = [original substringWithRange:NSMakeRange(charIdx++, 1)]; 

     bytesRead += [character lengthOfBytesUsingEncoding:targetEncoding]; 

     if (bytesRead <= maxBytesToRead) 
      [truncatedString appendString:character]; 
    } 

    return truncatedString; 
} 

EDIT: Kod może zostać przepisany w następujący sposób:

NSString *original = [_postDictionay objectForKey:@"message"]; 

NSArray *characters = [[original componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != ''"]]; 

NSArray *truncatedCharacters = [characters subarrayWithRange:range]; 

NSString *truncated = [NSString stringWithFormat:@"%@...", [truncatedCharacters componentsJoinedByString:@" "]]; 
+0

Pierwotnie korzystałem z substringWithRange, co spowodowałoby obcięcie znaku unicode dosłownie na pół ze względu na brak lepszego wyjaśnienia. Nie wiem, że substringToIndex zachowałoby charakter. Myśli? –

+0

Po prostu wypróbowałem substringToIndex i uzyskałem te same niefortunne wyniki, co w przypadku substringWithRange –

+0

Hm ... w jaki sposób tworzysz 'NSString'? Czy jesteś pewien, że określiłeś właściwe kodowanie podczas tworzenia? – fumoboy007