2013-04-02 3 views
9

The Problemусечение строки, содержащая 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 Благодаря ответ, был в состоянии использовать одну простую функцию для моих потребностей!

-(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; 

} 

ответ

14

NSString имеет метод rangeOfComposedCharacterSequencesForRange, что вы можете использовать, чтобы найти диапазон окружения в строке, содержащей только полные составленные символы. Например

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

дает диапазон { 0, 2 }, потому что Эмодзи хранится в виде двух UTF-16 символов (суррогатной пара) в строке.

Примечание: Вы также можете проверить, если вы можете упростить свой первый цикл, используя

enumerateSubstringsInRange:options:usingBlock 

с опцией NSStringEnumerationByWords.

+0

Спасибо, Мартин! –

2

«обрезает строку на заданной длине» < - Вы имеете в виду длину, как длина байта или длины, как по количеству символов? Если последнее, то достаточно простое substringToIndex: (сначала проверьте границы, хотя). Если первое, то я боюсь, что вы будете делать что-то вроде:

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: Ваш код можно переписать следующим образом:

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

Я изначально использовал substringWithRange, и он бы разрезал символ Юникода буквально пополам, из-за отсутствия лучшего объяснения. Я не знаю, что substringToIndex сохранит символ. Мысли? –

+0

Просто попробовал substringToIndex и имел такие же неудачные результаты, как и с substringWithRange –

+0

Hm ... как вы создаете свой 'NSString'? Вы уверены, что указали правильную кодировку при ее создании? – fumoboy007