Лучший способ избежать символов, таких как новая строка и двойные кавычки, в NSString

Скажем, у меня есть NSString (или NSMutableString), содержащий:

I said "Hello, world!".
He said "My name's not World."

Как лучше всего это превратить:

I said \"Hello, world!\".\nHe said \"My name\'s not World.\"

Придется ли мне вручную использовать -replaceOccurrencesOfString:withString: снова и снова, чтобы экранировать символы, или есть более простой способ? Эти строки могут содержать символы из других алфавитов / языков.

Как это делается на других языках с другими строковыми классами?

Ответов (7)

Решение

Я не думаю, что существует какой-либо встроенный метод для «экранирования» определенного набора символов.

Если символы, которых вы хотите избежать, четко определены, я бы, вероятно, придерживался предложенного вами простого решения, грубо заменяя экземпляры символов.

Имейте в виду, что если в исходной строке уже есть экранированные символы, вы, вероятно, захотите избежать их «двойного экранирования». Один из способов добиться этого - пройти и «отключить экранирование» любых экранированных символьных строк в строке, прежде чем снова экранировать их все.

Если вам нужно поддерживать переменный набор экранированных символов, взгляните на методы NSScanner «scanUpToCharactersFromSet: intoString:» и «scanCharactersFromSet: intoString:». Вы можете использовать эти методы в NSScanner для перемещения по строке, копируя части из раздела «scanUpTo» в изменяемую строку без изменений и копируя части из определенного набора символов только после их экранирования.

Это фрагмент, который я использовал в прошлом, и он работает довольно хорошо:

- (NSString *)escapeString:(NSString *)aString
{
    NSMutableString *returnString = [[NSMutableString alloc] init];

    for(int i = 0; i < [aString length]; i++) {

        unichar c = [aString characterAtIndex:i];

        // if char needs to be escaped
        if((('\\' == c) || ('\'' == c)) || ('"' == c)) {
            [returnString appendFormat:@"\\%c", c];            
        } else {
            [returnString appendFormat:@"%c", c];
        }
    }

    return [returnString autorelease];   
}

stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding

Возможно, вы даже захотите изучить использование библиотеки регулярных выражений (доступно множество вариантов, RegexKit - популярный выбор). Не должно быть слишком сложно найти заранее написанное регулярное выражение для escape-строк, которое обрабатывает особые случаи, такие как существующие экранированные символы.

Это позволит избежать двойных кавычек в NSString:

NSString *escaped = [originalString stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];

Так что будьте осторожны и избегайте escape-символа ...

Сделай это:

NSString * encodedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
    NULL,
    (CFStringRef)unencodedString,
    NULL,
    (CFStringRef)@"!*'();:@&=+$,/?%#[]",
    kCFStringEncodingUTF8 );

Ссылка: http://simonwoodside.com/weblog/2009/4/22/how_to_really_url_encode/

Я думаю, что в подобных случаях полезно работать с символом за раз, либо в байтах UniChars, либо в байтах UTF8. Если вы используете UTF-8, то vis(3) большую часть работы он сделает за вас (см. Ниже). Могу я спросить, почему вы хотите избежать одинарных кавычек в строке с двойными кавычками? Как вы планируете обрабатывать многобайтовые символы? В приведенном ниже примере я использую UTF-8, кодируя 8-битные символы с помощью восьмеричных escape-символов C-Style. Это также можно отменить с помощью unvis(3) .

#import <Foundation/Foundation.h>
#import <vis.h>

@interface NSString (Escaping)

- (NSString *)stringByEscapingMetacharacters;

@end

@implementation NSString (Escaping)

- (NSString *)stringByEscapingMetacharacters
{
    const char *UTF8Input = [self UTF8String];
    char *UTF8Output = [[NSMutableData dataWithLength:strlen(UTF8Input) * 4 + 1 /* Worst case */] mutableBytes];
    char ch, *och = UTF8Output;

    while ((ch = *UTF8Input++))
        if (ch == '\'' || ch == '\'' || ch == '\\' || ch == '"')
        {
            *och++ = '\\';
            *och++ = ch;
        }
        else if (isascii(ch))
            och = vis(och, ch, VIS_NL | VIS_TAB | VIS_CSTYLE, *UTF8Input);
        else
            och+= sprintf(och, "\\%03hho", ch);

    return [NSString stringWithUTF8String:UTF8Output];
}

@end

int
main(int argc, const char *argv[])
{
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    NSLog(@"%@", [@"I said \"Hello, world!\".\nHe said \"My name's not World.\"" stringByEscapingMetacharacters]);

    [pool drain];
    return 0;
}