问题
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