Swift - кодировать URL

216

Если я кодирую строку следующим образом:

var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)

он не выходит из косой черты /.

Я искал и нашел этот код Objective C:

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

Есть ли более простой способ кодирования URL-адреса, а если нет, как это записать в Swift?

Теги:
urlencode

13 ответов

490
Лучший ответ

Свифт 3

В Swift 3 есть addingPercentEncoding

let originalString = "test/test"
let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(escapedString!)

Выход:

Тест% 2Ftest

Свифт 1

В iOS 7 и выше есть stringByAddingPercentEncodingWithAllowedCharacters

var originalString = "test/test"
var escapedString = originalString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
println("escapedString: \(escapedString)")

Выход:

Тест% 2Ftest

Ниже приведены полезные (инвертированные) наборы символов:

URLFragmentAllowedCharacterSet  "#%<>[\]^'{|}
URLHostAllowedCharacterSet      "#%/<>?@\^'{|}
URLPasswordAllowedCharacterSet  "#%/:<>?@[\]^'{|}
URLPathAllowedCharacterSet      "#%;<>?[\]^'{|}
URLQueryAllowedCharacterSet     "#%<>[\]^'{|}
URLUserAllowedCharacterSet      "#%/:<>?@[\]^'

Если вы хотите экранировать другой набор символов, создайте набор:
Пример с добавленным символом "=":

var originalString = "test/test=42"
var customAllowedSet =  NSCharacterSet(charactersInString:"=\"#%/<>?@\\^'{|}").invertedSet
var escapedString = originalString.stringByAddingPercentEncodingWithAllowedCharacters(customAllowedSet)
println("escapedString: \(escapedString)")

Выход:

Тест% 2Ftest% 3D42

Пример проверки символов ascii, не входящих в набор:

func printCharactersInSet(set: NSCharacterSet) {
    var characters = ""
    let iSet = set.invertedSet
    for i: UInt32 in 32..<127 {
        let c = Character(UnicodeScalar(i))
        if iSet.longCharacterIsMember(i) {
            characters = characters + String(c)
        }
    }
    print("characters not in set: \'\(characters)\'")
}
  • 0
    Вы правы. Я неправильно понимаю документ ...
  • 0
    А если я тоже хочу добавить знак равенства?
Показать ещё 16 комментариев
37

Вы можете использовать URLComponents, чтобы избежать того, чтобы вручную избежать escape-строки запроса:

let scheme = "https"
let host = "www.google.com"
let path = "/search"
let queryItem = URLQueryItem(name: "q", value: "Formula One")


var urlComponents = URLComponents()
urlComponents.scheme = scheme
urlComponents.host = host
urlComponents.path = path
urlComponents.queryItems = [queryItem]

if let url = urlComponents.url {
    print(url)   // "https://www.google.com/search?q=Formula%20One"
}

extension URLComponents {
    init(scheme: String, host: String, path: String, queryItems: [URLQueryItem]) {
        self.init()
        self.scheme = scheme
        self.host = host
        self.path = path
        self.queryItems = queryItems
    }
}

if let url = URLComponents(scheme: "https",
                             host: "www.google.com",
                             path: "/search",
                       queryItems: [URLQueryItem(name: "q", value: "Formula One")]).url {

    print(url)  // https://www.google.com/search?q=Formula%20One
}
  • 6
    Этот ответ требует большего внимания, так как есть проблемы со всеми остальными (хотя, чтобы быть справедливым, они могли быть лучшей практикой в то время).
  • 1
    К сожалению, URLQueryItem не всегда правильно кодирует. Например, Formula+One будет закодирована в Formula+One , которая будет декодирована в Formula One . Поэтому будьте осторожны со знаком плюс.
Показать ещё 1 комментарий
33

Swift 3:

let allowedCharacterSet = (CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[] ").inverted)

if let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) {
//do something with escaped string
}
  • 2
    Вам нужно включить `` (пробел) в строку символов
  • 1
    Вы также должны включить ^
19

Swift 3:

let originalString = "http://www.ihtc.cc?name=htc&title=iOS开发工程师"

1. encodingQuery:

let escapedString = originalString.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)

результат:

"http://www.ihtc.cc?name=htc&title=iOS%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88" 

2. encodingURL:

let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)

результат:

"http:%2F%2Fwww.ihtc.cc%3Fname=htc&title=iOS%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88"
  • 0
    Я использовал первое решение, но хочу вернуть текст обратно, как iOS 开发 工程师.
  • 0
    .urlQueryAllowed работал для меня, спасибо
Показать ещё 3 комментария
15

Swift 4

Для кодирования параметра в URL я нахожу, используя .alphanumerics символов .alphanumerics самый простой вариант:

let encoded = parameter.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
let url = "http://www.example.com/?name=\(encoded!)"

Использование любого из стандартных наборов символов для кодирования URL (например, URLQueryAllowedCharacterSet или URLHostAllowedCharacterSet) не будет работать, поскольку они не исключают символы = или &.

Обратите внимание, что при использовании .alphanumerics он будет кодировать некоторые символы, которые не нужно кодировать (например, -, ., _ Или ~ - - см. 2.3. Незарезервированные символы в RFC 3986). Я считаю, что использование .alphanumerics проще, чем создание собственного набора символов, и я не против некоторых дополнительных символов, которые нужно кодировать. Если это вас беспокоит, создайте пользовательский набор символов, как описано в разделе Как в процентах кодировать строку URL, например, так:

var allowed = CharacterSet.alphanumerics
allowed.insert(charactersIn: "-._~") // as per RFC 3986
let encoded = parameter.addingPercentEncoding(withAllowedCharacters: allowed)
let url = "http://www.example.com/?name=\(encoded!)"

Предупреждение: encoded параметр принудительно развернут. Для недопустимой строки Unicode может произойти сбой. См. Почему возвращаемое значение String.addingPercentEncoding() необязательно? , Вместо encoded! принудительной распаковки encoded! вы можете использовать encoded?? "" encoded?? "" или используйте, if let encoded =...

11

Все одинаково

var str = CFURLCreateStringByAddingPercentEscapes(
    nil,
    "test/test",
    nil,
    "!*'();:@&=+$,/?%#[]",
    CFStringBuiltInEncodings.UTF8.rawValue
)

// test%2Ftest
  • 0
    Вы не .bridgeToOvjectiveC() второй аргумент .bridgeToOvjectiveC() и не получили "Невозможно преобразовать тип выражения 'CFString!' набрать 'CFString!' "?
  • 0
    @ Kreiri Зачем это нужно? И детская площадка, и REPL довольны моим кодом.
Показать ещё 2 комментария
9

Swift 4 (не проверено - прокомментируйте, работает ли оно или нет. Спасибо @sumizome за предложение)

var allowedQueryParamAndKey = NSCharacterSet.urlQueryAllowed
allowedQueryParamAndKey.remove(charactersIn: ";/?:@&=+$, ")
paramOrKey.addingPercentEncoding(withAllowedCharacters: allowedQueryParamAndKey)

Свифт 3

let allowedQueryParamAndKey =  NSCharacterSet.urlQueryAllowed.remove(charactersIn: ";/?:@&=+$, ")
paramOrKey.addingPercentEncoding(withAllowedCharacters: allowedQueryParamAndKey)

Swift 2.2 (заимствование у Zaph и исправление ключа URL-адреса и значений параметров)

var allowedQueryParamAndKey =  NSCharacterSet(charactersInString: ";/?:@&=+$, ").invertedSet
paramOrKey.stringByAddingPercentEncodingWithAllowedCharacters(allowedQueryParamAndKey)

Пример:

let paramOrKey = "https://some.website.com/path/to/page.srf?a=1&b=2#top"
paramOrKey.addingPercentEncoding(withAllowedCharacters: allowedQueryParamAndKey)
// produces:
"https%3A%2F%2Fsome.website.com%2Fpath%2Fto%2Fpage.srf%3Fa%3D1%26b%3D2%23top"

Это более короткая версия ответа Брайана Чена. Я предполагаю, что urlQueryAllowed разрешает управляющие символы, с помощью которых все в порядке, если только они не образуют часть ключа или значения в строке запроса, и в этот момент их необходимо экранировать.

  • 2
    Мне нравится решение Swift 3, но оно не работает для меня в Swift 4: «Невозможно использовать мутирующий член в неизменяемом значении:« urlQueryAllowed »является свойством только для получения».
  • 0
    @ MariánČerný просто сделайте изменяемый CharacterSet (с помощью var ), а затем вызовите .remove для него на втором этапе.
8

Swift 4:

Это зависит от правил кодирования, за которыми следует ваш сервер.

Apple предлагает этот метод класса, но он не сообщает о каком-то RCF-протоколе.

var escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!

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

  • $ (Знак доллара) становится% 24
  • & (Амперсанд) становится% 26
  • + (Плюс) становится% 2B
  • , (Comma) становится% 2C
  • : (Colon) становится% 3A
  • ; (Полуколон) становится% 3B
  • = (Равно) становится% 3D
  • ? (Question Mark) становится% 3F
  • @(Commercial A/At) становится% 40

Другими словами, говоря о кодировке URL, вы должны следовать протоколу RFC 1738.

И Swift не покрывает кодировку + char, например, но он хорошо работает с этими тремя @:? символы.

Итак, чтобы правильно кодировать каждый ваш параметр, параметра .urlHostAllowed недостаточно, вы должны также добавить специальные символы, например:

encodedParameter = parameter.replacingOccurrences(of: "+", with: "%2B")

Надеюсь, это поможет кому-то, кто сходит с ума, искать эту информацию.

  • 0
    Ваша реализация совершенно неверна. Как будет закодирован параметр "věž"?
  • 0
    Спасибо, я обновил свой ответ.
4

Мне нужно это, поэтому я написал расширение String, которое позволяет использовать строки URLEncoding, а также более общую конечную цель: преобразование словаря параметров в параметр URL-адреса стиля "GET":

extension String {
    func URLEncodedString() -> String? {
        var escapedString = self.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
        return escapedString
    }
    static func queryStringFromParameters(parameters: Dictionary<String,String>) -> String? {
        if (parameters.count == 0)
        {
            return nil
        }
        var queryString : String? = nil
        for (key, value) in parameters {
            if let encodedKey = key.URLEncodedString() {
                if let encodedValue = value.URLEncodedString() {
                    if queryString == nil
                    {
                        queryString = "?"
                    }
                    else
                    {
                        queryString! += "&"
                    }
                    queryString! += encodedKey + "=" + encodedValue
                }
            }
        }
        return queryString
    }
}

Наслаждайтесь!

  • 2
    Это не кодирует знак «&». Использование «&» в параметре приведет к увеличению строки запроса
  • 0
    Это неправильно, это не кодирует & или = в параметрах. Проверьте мое решение вместо этого.
2

Этот работает для меня.

func stringByAddingPercentEncodingForFormData(plusForSpace: Bool=false) -> String? {

    let unreserved = "*-._"
    let allowed = NSMutableCharacterSet.alphanumericCharacterSet()
    allowed.addCharactersInString(unreserved)

    if plusForSpace {
        allowed.addCharactersInString(" ")
    }

    var encoded = stringByAddingPercentEncodingWithAllowedCharacters(allowed)

    if plusForSpace {
        encoded = encoded?.stringByReplacingOccurrencesOfString(" ", withString: "+")
    }
    return encoded
}

Я нашел функцию выше по этой ссылке: http://useyourloaf.com/blog/how-to-percent-encode-a-url-string/.

0

SWIFT 4.2

Иногда это происходит только потому, что в слаге ИЛИ отсутствует кодировка URL для параметров, проходящих через API URL.

let myString = self.slugValue
                let csCopy = CharacterSet(bitmapRepresentation: CharacterSet.urlPathAllowed.bitmapRepresentation)
                let escapedString = myString!.addingPercentEncoding(withAllowedCharacters: csCopy)!
                //always "info:hello%20world"
                print(escapedString)

ПРИМЕЧАНИЕ: не забудьте изучить bitmapRepresentation.

0

Swift 4: Вы можете закодировать URL с помощью этого:

 if let encoded = url.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),
        let url = URL(string: encoded)
    {
        print(url)
        // if you want convert URL to String 
        url = url.absoluteString
    }
0

Это работает для меня в Swift 4.2. В случае использования берется URL-адрес из буфера обмена или аналогичный, который, возможно, уже содержит экранированные символы, но также содержит символы Unicode, которые могут привести к сбою URL(string:).

func encodedUrl(from string: String) -> URL? {
    // Remove preexisting encoding
    guard let decodedString = string.removingPercentEncoding,
        // Reencode, to revert decoding while encoding missed characters
        let percentEncodedString = decodedString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
            // Coding failed
            return nil
    }
    // Create URL from encoded string, or nil if failed
    return URL(string: percentEncodedString)
}

let urlText = "https://www.example.com/폴더/search?q=123&foo=bar&multi=eggs+and+ham&hangul=한글&spaced=lovely%20spam&illegal=<>"
let url = encodedUrl(from: urlText)

Значение url в конце: https://www.example.com/%ED%8F%B4%EB%8D%94/search?q=123&foo=bar&multi=eggs+and+ham&hangul=%ED%95%9C%EA%B8%80&spaced=lovely%20spam&illegal=%3C%3E

Обратите внимание, что %20 и + интервал сохраняются, символы Unicode кодируются, а %20 в исходном urlText не кодируется дважды.

Предостережение: это ярлык, так как он использует .urlQueryAllowed для всего URL; он также предполагает, что URL уже работал в браузере. Используйте с умом! Я также открыт для предложений (я остановился на том, чтобы URLComponents весь URL с помощью URLComponents, применить отдельные наборы символов и пересобрать).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню