Swift - кодировать URL
Если я закодирую строку, как это:
var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
это не ускользнет от косой черты /
,
Я искал и нашел этот код Objective C:
NSString *encodedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)unencodedString,
NULL,
(CFStringRef)@"!*'();:@&=+$,/?%#[]",
kCFStringEncodingUTF8 );
Есть ли более простой способ закодировать URL, и если нет, то как мне написать это в Swift?
21 ответ
Свифт 3
В Swift 3 есть addingPercentEncoding
var originalString = "test/test"
var 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)\'")
}
Вы можете использовать URLComponents, чтобы избежать необходимости вручную экранировать процент вашей строки запроса:
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
}
Swift 4
Для кодирования параметра в URL я нахожу с помощью .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 ?? ""
или использовать if let encoded = ...
,
Свифт 3:
let allowedCharacterSet = (CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[] ").inverted)
if let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) {
//do something with escaped string
}
Свифт 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"
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
разрешает управляющие символы, с помощью которых все в порядке, если только они не являются частью ключа или значения в строке запроса, и в этот момент их необходимо экранировать.
Свифт 4:
Это зависит от правил кодирования вашего сервера.
Apple предлагает этот метод класса, но он не сообщает, какой протокол RCF следует.
var escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
Следуя этому полезному инструменту, вы должны гарантировать кодирование этих символов для ваших параметров:
- $ (Знак доллара) становится% 24
- & (Амперсанд) становится% 26
- + (Плюс) становится% 2B
- , (Запятая) становится% 2C
- : (Двоеточие) становится% 3A
- ; (Полуколон) становится% 3B
- = (Равно) становится% 3D
- ? (Знак вопроса) становится% 3F
- @ (Коммерческий A / At) становится%40
Другими словами, говоря о кодировании URL, вы должны следовать протоколу RFC 1738.
И Swift не охватывает, например, кодировку + char, но она хорошо работает с этими тремя @:? символы.
Таким образом, чтобы правильно кодировать каждый ваш параметр, .urlHostAllowed
опция не достаточна, вы должны добавить также специальные символы, например:
encodedParameter = parameter.replacingOccurrences(of: "+", with: "%2B")
Надеюсь, что это помогает кому-то, кто сходит с ума, чтобы искать эту информацию.
Все одинаково
var str = CFURLCreateStringByAddingPercentEscapes(
nil,
"test/test",
nil,
"!*'();:@&=+$,/?%#[]",
CFStringBuiltInEncodings.UTF8.rawValue
)
// test%2Ftest
Swift 4.2
Быстрое решение в одну строку. замещать originalString
со строкой, которую вы хотите кодировать.
var encodedString = originalString.addingPercentEncoding(withAllowedCharacters: CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[]{} ").inverted)
Это работает для меня в 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 уже работал в браузере. Используйте с умом! Я также открыт для предложений (я остановился на том, чтобы разделить весь URL с помощью URLComponents
, применение отдельных наборов символов и повторная сборка).
Для Swift 5 завершить строку кода
func escape(string: String) -> String {
let allowedCharacters = string.addingPercentEncoding(withAllowedCharacters: CharacterSet(charactersIn: ":=\"#%/<>?@\\^`{|}").inverted) ?? ""
return allowedCharacters
}
Как пользоваться?
let strEncoded = self.escape(string: "http://www.edamam.com/ontologies/edamam.owl#recipe_e2a1b9bf2d996cbd9875b80612ed9aa4")
print("escapedString: \(strEncoded)")
Я сам нуждался в этом, поэтому я написал расширение 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
}
}
Наслаждайтесь!
Этот работает на меня.
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/.
let Url = URL(string: urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")
Ни один из этих ответов не помог мне. В нашем приложении происходил сбой, когда URL-адрес содержал неанглийские символы.
let unreserved = "-._~/?%$!:"
let allowed = NSMutableCharacterSet.alphanumeric()
allowed.addCharacters(in: unreserved)
let escapedString = urlString.addingPercentEncoding(withAllowedCharacters: allowed as CharacterSet)
В зависимости от параметров того, что вы пытаетесь сделать, вы можете просто создать свой собственный набор символов. Вышеупомянутое позволяет использовать английские символы и-._~/?%$!:
Мне помогло то, что я создал отдельный NSCharacterSet
и использовал его в строке в кодировке UTF-8, т.е. textToEncode, чтобы сгенерировать требуемый результат:
var queryCharSet = NSCharacterSet.urlQueryAllowed
queryCharSet.remove(charactersIn: "+&?,:;@+=$*()")
let utfedCharacterSet = String(utf8String: textToEncode.cString(using: .utf8)!)!
let encodedStr = utfedCharacterSet.addingPercentEncoding(withAllowedCharacters: queryCharSet)!
let paramUrl = "https://api.abc.eu/api/search?device=true&query=\(escapedStr)"
Раньше у меня была проблема с такими строками%5B×××%5D
(где некоторые части уже были закодированы).
Мне потребовался час, чтобы понять, что это можно легко решить с помощью этого:
extension String {
var decodeThenEncodeURL: String {
removingPercentEncoding?.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? self
}
}
- вход:
https://www.google.com/search?q=%5B×××%5D
- выход:
https://www.google.com/search?q=%5B%C3%97%C3%97%C3%97%5D
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
,
Swift 5 Можно попробовать. Параметр afURLQueryAllowed, если вы хотите кодировать строку, как показано ниже
let testString = "6hAD9/RjY+SnGm&B"
let escodedString = testString.addingPercentEncoding(withAllowedCharacters: .afURLQueryAllowed)
print(escodedString!)
// закодированная строка будет иметь вид en6hAD9%2FRjY%2BSnGm%26B
версия:Swift 5
// space convert to +
let mstring = string.replacingOccurrences(of: " ", with: "+")
// remove special character
var allowedQueryParamAndKey = NSCharacterSet.urlQueryAllowed
allowedQueryParamAndKey.remove(charactersIn: "!*'\"();:@&=+$,/?%#[]% ")
return mstring.addingPercentEncoding(withAllowedCharacters: allowedQueryParamAndKey) ?? mstring
Это лучше и авторитетнее. он цитирует Almofire URLEncodedFormEncoder.swift
extension CharacterSet {
/// Creates a CharacterSet from RFC 3986 allowed characters.
///
/// RFC 3986 states that the following characters are "reserved" characters.
///
/// - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
/// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
///
/// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
/// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
/// should be percent-escaped in the query string.
public static let afURLQueryAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters)
}()
}