如何缩短可能包括例如 NSString表情符号为 HFS+ 文件名允许的最大长度
How to shorten an NSString that might include e.g. Emojis to the maximal length allowed for a HFS+ filename
Apple 文档说:
[...] 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.
如何限制 NSString 的长度使其真正短于 255 个字符,即使它包含可能需要多个字符才能存储的符号?
我在下面添加我当前的实现。如果我添加例如表情符号添加到字符串,而 length
回答结果字符串将远小于 255,它仍然太长而无法被 NSSavePanel
接受为文件名。
NSRange stringRange = {0, MIN([fileName length], 255)};
stringRange = [fileName rangeOfComposedCharacterSequencesForRange:stringRange];
fileName = [fileName substringWithRange:stringRange];
根据@JoshCaswell 的建议,我确实将 this answer 修改为类似的问题。它显然有效(我写了几个测试),但对我来说似乎很奇怪。如此明显的任务不可能如此复杂地实现?
// 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];
}
rangeOfComposedCharacterSequencesForRange:
基本上与你想要的相反:你给它一个计算 255 个组成字符的范围,它给你包含这些字符的字节范围,最终可能比你要。
不幸的是,要进行相反的操作,您必须手动计算字节数。然而,对于 enumerateSubstringsInRange:options:usingBlock:
,这并不难。为选项传递 NSStringEnumerationByComposedCharacterSequences
可以准确地给出它所说的:依次组成每个字符。然后您可以使用 lengthOfBytesUsingEncoding:
计算每个的大小,传递您将使用的最终编码(大概是 UTF-8)。添加字节,跟踪基于字符的索引,当你看到太多时停止。
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];
Apple 文档说:
[...] 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.
如何限制 NSString 的长度使其真正短于 255 个字符,即使它包含可能需要多个字符才能存储的符号?
我在下面添加我当前的实现。如果我添加例如表情符号添加到字符串,而 length
回答结果字符串将远小于 255,它仍然太长而无法被 NSSavePanel
接受为文件名。
NSRange stringRange = {0, MIN([fileName length], 255)};
stringRange = [fileName rangeOfComposedCharacterSequencesForRange:stringRange];
fileName = [fileName substringWithRange:stringRange];
根据@JoshCaswell 的建议,我确实将 this answer 修改为类似的问题。它显然有效(我写了几个测试),但对我来说似乎很奇怪。如此明显的任务不可能如此复杂地实现?
// 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];
}
rangeOfComposedCharacterSequencesForRange:
基本上与你想要的相反:你给它一个计算 255 个组成字符的范围,它给你包含这些字符的字节范围,最终可能比你要。
不幸的是,要进行相反的操作,您必须手动计算字节数。然而,对于 enumerateSubstringsInRange:options:usingBlock:
,这并不难。为选项传递 NSStringEnumerationByComposedCharacterSequences
可以准确地给出它所说的:依次组成每个字符。然后您可以使用 lengthOfBytesUsingEncoding:
计算每个的大小,传递您将使用的最终编码(大概是 UTF-8)。添加字节,跟踪基于字符的索引,当你看到太多时停止。
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];