How to shorten an NSString that might include e.g. Emojis to the maximal length allowed for a HFS+ filename

眉间皱痕 提交于 2019-12-13 00:55:22

问题


Apples documentation says:

[...] current file systems such as HFS+ (used by Mac OS X) allow you to create filenames with a 255-character limit [...] symbols may actually take up to the equivalent of nine English characters to store [...] This should be considered when attempting to create longer names.

How do I limit the length of a NSString in a way that it is truly shorter than 255 characters, even when it includes symbols that might take more than one character to store?

I add my current implementation below. If i add e.g. emojis to the string, while length answers the resulting string would be by far smaller than 255, it is still too long to be accepted by a NSSavePanel as file name.

NSRange stringRange = {0, MIN([fileName length], 255)};
stringRange = [fileName rangeOfComposedCharacterSequencesForRange:stringRange];
fileName = [fileName substringWithRange:stringRange];

回答1:


rangeOfComposedCharacterSequencesForRange: is basically doing the opposite of what you want: you give it a range that counts 255 composed characters, and it gives you the byte range that encompasses those, which might end up being much more than you want.

Unfortunately to do the reverse, you have to count the bytes manually. This isn't too hard, however, with enumerateSubstringsInRange:options:usingBlock:. Passing NSStringEnumerationByComposedCharacterSequences for the options gives you exactly what it says: each composed character in turn. You can then count the size of each with lengthOfBytesUsingEncoding:, passing the final encoding you'll be using (presumably UTF-8). Add up the bytes, keeping track of the character-based index, and stop when you've seen too many.

NSString * s = /* String containing multibyte characters */;
NSUInteger maxBytes = ...;
__block NSUInteger seenBytes = 0;
__block NSUInteger truncLength = 0;
NSRange fullLength = (NSRange){0, [s length]};

[s enumerateSubstringsInRange:fullLength
                      options:NSStringEnumerationByComposedCharacterSequences
                   usingBlock:
    ^(NSString *substring, NSRange substringRange,
      NSRange _, BOOL *stop)
    {
        seenBytes += [substring lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
        if( seenBytes > maxBytes ){
            *stop = YES;
            return;
        }
        else {
            truncLength += substringRange.length;
        }
}];

NSString * truncS = [s substringToIndex:truncLength];



回答2:


As suggested by @JoshCaswell, I did modify this answer to a similar question. It apparently does work (I wrote several tests), but it seems strange to me. Such an obvious task cannot be so complicated to achieve?

// filename contains the NSString that should be shortened
NSMutableString *truncatedString = [NSMutableString string];
NSUInteger bytesRead = 0;
NSUInteger charIdx = 0;

while (bytesRead < 250 && charIdx < [fileName length])
{
    NSRange range = [fileName rangeOfComposedCharacterSequencesForRange:NSMakeRange(charIdx, 1)];
    NSString *character = [fileName substringWithRange:NSMakeRange(charIdx, range.length)];
    bytesRead += [character lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    charIdx = charIdx + range.length;
    if (bytesRead <= 250)
        [truncatedString appendString:character];
}


来源:https://stackoverflow.com/questions/31733100/how-to-shorten-an-nsstring-that-might-include-e-g-emojis-to-the-maximal-length

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!