Objective-c Процент iPhone для кодирования строки?

Я хотел бы получить процентную кодированную строку для этих конкретных букв, как это сделать в objective-c?

Reserved characters after percent-encoding
!   *   '   (   )   ;   :   @   &   =   +   $   ,   /   ?   #   [   ]
%21 %2A %27 %28 %29 %3B %3A %40 %26 %3D %2B %24 %2C %2F %3F %23 %5B %5D

Прокси-кодировка в процентах

Проконсультируйтесь с этой строкой и посмотрите, действительно ли она работает:

myURL = @"someurl/somecontent"

Я хотел бы, чтобы строка выглядела так:

myEncodedURL = @"someurl%2Fsomecontent"

Я попытался с stringByAddingPercentEscapesUsingEncoding: NSASCIIStringEncoding уже, но он не работает, результат все тот же, что и исходная строка. Пожалуйста, совет.

Ответ 1

Я обнаружил, что оба stringByAddingPercentEscapesUsingEncoding: и CFURLCreateStringByAddingPercentEscapes() неадекватны. Метод NSString пропускает довольно много символов, а функция CF позволяет вам указывать только те (конкретные) символы, которые вы хотите избежать. Правильная спецификация заключается в том, чтобы избежать всех символов, кроме небольшого набора.

Чтобы исправить это, я создал метод категории NSString для правильной кодировки строки. Он будет кодировать все EXCEPT [a-zA-Z0-9.-_~] и будет также кодировать пробелы как + (согласно эта спецификация). Он также будет правильно обрабатывать кодировку символов юникода.

- (NSString *) URLEncodedString_ch {
    NSMutableString * output = [NSMutableString string];
    const unsigned char * source = (const unsigned char *)[self UTF8String];
    int sourceLen = strlen((const char *)source);
    for (int i = 0; i < sourceLen; ++i) {
        const unsigned char thisChar = source[i];
        if (thisChar == ' '){
            [output appendString:@"+"];
        } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' || 
                   (thisChar >= 'a' && thisChar <= 'z') ||
                   (thisChar >= 'A' && thisChar <= 'Z') ||
                   (thisChar >= '0' && thisChar <= '9')) {
            [output appendFormat:@"%c", thisChar];
        } else {
            [output appendFormat:@"%%%02X", thisChar];
        }
    }
    return output;
}

Ответ 2

Теперь у iOS 7 SDK есть лучшая альтернатива stringByAddingPercentEscapesUsingEncoding, которая позволяет указать, что вы хотите, чтобы все символы были экранированы, кроме определенных разрешенных. Он работает хорошо, если вы создаете URL-адрес по частям:

NSString * unescapedQuery = [[NSString alloc] initWithFormat:@"?myparam=%d", numericParamValue];
NSString * escapedQuery = [unescapedQuery stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSString * urlString = [[NSString alloc] initWithFormat:@"http://ExampleOnly.com/path.ext%@", escapedQuery];

Хотя реже, чем другие части URL-адреса будут переменными, в категории NSURLUtilities есть константы:

[NSCharacterSet URLHostAllowedCharacterSet]
[NSCharacterSet URLUserAllowedCharacterSet]
[NSCharacterSet URLPasswordAllowedCharacterSet]
[NSCharacterSet URLPathAllowedCharacterSet]
[NSCharacterSet URLFragmentAllowedCharacterSet]

[NSCharacterSet URLQueryAllowedCharacterSet] включает все символов, разрешенных в части запроса URL (часть, начинающаяся с ? и до # для фрагмента, если таковая имеется), включая ? и символы & или =, которые используются для разграничения имен и значений параметров. Для параметров запроса с буквенно-цифровыми значениями любой из этих символов может быть включен в значения переменных, используемых для построения строки запроса. В этом случае каждая часть строки запроса должна быть экранирована, что требует немного больше работы:

NSMutableCharacterSet * URLQueryPartAllowedCharacterSet; // possibly defined in class extension ...

// ... and built in init or on first use
URLQueryPartAllowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
[URLQueryPartAllowedCharacterSet removeCharactersInString:@"&+=?"]; // %26, %3D, %3F

// then escape variables in the URL, such as values in the query and any fragment:
NSString * escapedValue = [anUnescapedValue stringByAddingPercentEncodingWithAllowedCharacters:URLQueryPartAllowedCharacterSet];
NSString * escapedFrag = [anUnescapedFrag stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
NSString * urlString = [[NSString alloc] initWithFormat:@"http://ExampleOnly.com/path.ext?myparam=%@#%@", escapedValue, escapedFrag];
NSURL * url = [[NSURL alloc] initWithString:urlString];

unescapedValue может быть даже целым URL-адресом, например, для обратного вызова или перенаправления:

NSString * escapedCallbackParamValue = [anAlreadyEscapedCallbackURL stringByAddingPercentEncodingWithAllowedCharacters:URLQueryPartAllowedCharacterSet];
NSURL * callbackURL = [[NSURL alloc] initWithString:[[NSString alloc] initWithFormat:@"http://ExampleOnly.com/path.ext?callback=%@", escapedCallbackParamValue]];

Примечание. Не используйте NSURL initWithScheme:(NSString *)scheme host:(NSString *)host path:(NSString *)path для URL-адреса с строкой запроса, потому что он добавит больше процентов экранов в путь.

Ответ 3

NSString stringByAddingPercentEscapesUsingEncoding: выглядит так, как вы.

РЕДАКТИРОВАТЬ. Здесь приведен пример использования CFURLCreateStringByAddingPercentEscapes. originalString может быть либо NSString, либо CFStringRef.

CFStringRef newString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, originalString, NULL, CFSTR("!*'();:@&[email protected],/?#[]"), kCFStringEncodingUTF8);

Обратите внимание, что это не проверено. Вы должны взглянуть на страницу документации , чтобы убедиться, что вы понимаете семантику выделения памяти для CFStringRef, идею бесплатного моста, и т.д.

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

Я делаю этот ответ в вики сообщества, чтобы люди с более глубокими знаниями о CoreFoundation могли делать улучшения.

Ответ 4

NSString *encodedString = [myString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];

Он не заменит вашу строку inline; он вернет новую строку. Это подразумевает тот факт, что метод начинается со слова "строка". Это удобный метод для создания экземпляра нового экземпляра NSString на основе текущей NSString.

Заметьте, что новая строка будет autorelease 'd, поэтому не звоните в нее, когда закончите с ней.

Ответ 5

Если вы используете библиотеку ASI HttpRequest в своей программе objective-c, которую я не могу рекомендовать достаточно высоко, то вы можете использовать команду "encodeURL" вспомогательный API" на свой объект ASIFormDataRequest. К сожалению, API не является статическим, поэтому, возможно, стоит создать расширение, используя его реализацию в вашем проекте.

Код, скопированный прямо из ASIFormDataRequest.m для реализации encodeURL,:

- (NSString*)encodeURL:(NSString *)string
{
    NSString *newString = NSMakeCollectable([(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding([self stringEncoding])) autorelease]);
    if (newString) {
        return newString;
    }
    return @"";
}

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

Ответ 6

Следуя стандарту RFC3986, вот что я использую для кодирования компонентов URL:

// https://tools.ietf.org/html/rfc3986#section-2.2
let rfc3986Reserved = NSCharacterSet(charactersInString: "!*'();:@&=+$,/?#[]")
let encoded = "[email protected]".stringByAddingPercentEncodingWithAllowedCharacters(rfc3986Reserved.invertedSet)

Выход: email%2Bwith%2Bplus%40example.com

Ответ 7

Прежде чем я заметил Роб ответ, который, кажется, работает хорошо и предпочитает, как он чище, я пошел вперед и портировал Дэйв ответ на Свифт. Я оставлю его здесь, если кто-то заинтересован:

public extension String {

    // For performance, I've replaced the char constants with integers, as char constants don't work in Swift.

    var URLEncodedValue: String {
        let output = NSMutableString()
        guard let source = self.cStringUsingEncoding(NSUTF8StringEncoding) else {
            return self
        }
        let sourceLen = source.count

        var i = 0
        while i < sourceLen - 1 {
            let thisChar = source[i]
            if thisChar == 32 {
                output.appendString("+")
            } else if thisChar == 46 || thisChar == 45 || thisChar == 95 || thisChar == 126 ||
                (thisChar >= 97 && thisChar <= 122) ||
                (thisChar >= 65 && thisChar <= 90) ||
                (thisChar >= 48 && thisChar <= 57) {
                    output.appendFormat("%c", thisChar)
            } else {
                output.appendFormat("%%%02X", thisChar)
            }

            i++
        }

        return output as String
    }
}

Ответ 8

В Swift4:

 var str = "someurl/somecontent"

 let percentEncodedString = str.addingPercentEncoding(withAllowedCharacters: .alphanumerics)