Как сократить NSString, который может включать, например. Смайлики до максимальной длины, разрешенной для имени файла HFS+.

В документации Apple говорится:

[...] современные файловые системы, такие как HFS+ (используемая Mac OS X), позволяют создавать имена файлов с ограничением в 255 символов [...] символы могут фактически занимать до девяти английских символов для хранения [. ..] Это следует учитывать при попытке создать более длинные имена.

Как ограничить длину NSString таким образом, чтобы она действительно была короче 255 символов, даже если она включает символы, для хранения которых может потребоваться более одного символа?

Я добавляю свою текущую реализацию ниже. Если я добавлю, например. смайлики в строку, в то время как length отвечает, результирующая строка будет намного меньше 255, она все еще слишком длинная, чтобы быть принятой NSSavePanel в качестве имени файла.

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

person MartinW    schedule 30.07.2015    source источник
comment
rangeOfComposedCharacterSequencesForRange: в основном делает противоположное тому, что вы хотите: вы даете ему диапазон, который насчитывает 255 кодовых точек, и он дает вам диапазон байтов, который охватывает их, что может оказаться намного больше, чем вы хотите. Я не совсем уверен, как сделать обратный расчет; интересная маленькая головоломка.   -  person jscs    schedule 30.07.2015
comment
Я думаю, что ответ fumoboy007 на Truncate string, содержащий символы emoji или unicode на границах слов или символов, может быть вашим решением, хотя вы должны быть возможность сделать цикл красивее с помощью enumerateSubstringsInRange:options:usingBlock:   -  person jscs    schedule 30.07.2015
comment
@JoshCaswell Спасибо за подсказку. Это не совсем сработало, но посмотрите мой собственный ответ о том, как я его изменил. Я написал несколько тестов, но я все еще не уверен, могу ли я этому доверять. Невероятно, что нечто подобное может оказаться настолько сложным?   -  person MartinW    schedule 02.08.2015


Ответы (2)


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];
person jscs    schedule 02.08.2015

Как предложил @JoshCaswell, я изменил этот ответ на аналогичный вопрос. Вроде работает (я написал несколько тестов), но мне это кажется странным. Такая очевидная задача не может быть настолько сложной для достижения?

// 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];
}
person MartinW    schedule 01.08.2015