¿Cómo codifico una cadena URL?

Respuestas:

291

Lamentablemente, stringByAddingPercentEscapesUsingEncodingno siempre funciona al 100%. Codifica caracteres que no son URL pero deja solo los caracteres reservados (como barra oblicua /y ampersand &). Aparentemente, este es un error que Apple conoce, pero como aún no lo han solucionado, he estado usando esta categoría para codificar una cadena en url:

@implementation NSString (NSString_Extended)

- (NSString *)urlencode {
    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;
}

Usado así:

NSString *urlEncodedString = [@"SOME_URL_GOES_HERE" urlencode];

// Or, with an already existing string:
NSString *someUrlString = @"someURL";
NSString *encodedUrlStr = [someUrlString urlencode];

Esto también funciona:

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

Algunas buenas lecturas sobre el tema:

Objective-c ¿El porcentaje de iPhone codifica una cadena?
Objective-C y codificación Swift URL

http://cybersam.com/programming/proper-url-percent-encoding-in-ios
https://devforums.apple.com/message/15674#15674 http://simonwoodside.com/weblog/2009/4/ 22 / how_to_really_url_encode /

chown
fuente
12
¿Por qué usarías una categoría complicada como esta en lugar de solo desplegarte en CFURLCreateStringByAddingPercentEscapes () , donde puedes especificar explícitamente de qué caracteres quieres escapar siempre?
Lily Ballard
8
@KevinBallard porque la función CF funciona en un modelo inclusivo ("escapar de estos caracteres"), y generalmente desea trabajar en un modelo exclusivo ("escapar de todo excepto estos caracteres")
Dave DeLong
31
@DaveDeLong Eso puede ser donde lo obtuve. Ha sido una categoría estándar en todos mis proyectos durante un año más o menos, ¡así que imagino que puede haber sido la fuente original! He editado la redacción para que no parezca que estoy tratando de tomar el crédito por escribirla).
vestido el
44
¿Por qué? if (thisChar == '') {[output appendString: @ "+"]; } Sin esto, si codificará espacios con% 20 como era de esperar
Chris Stephens
127

Esto puede ser útil.

NSString *sampleUrl = @"http://www.google.com/search.jsp?params=Java Developer";
NSString* encodedUrl = [sampleUrl stringByAddingPercentEscapesUsingEncoding:
 NSUTF8StringEncoding];

Para iOS 7+, la forma recomendada es:

NSString* encodedUrl = [sampleUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];

Puede elegir el juego de caracteres permitido según el requisito del componente URL.

Nishant
fuente
77
Esto tampoco codifica correctamente los delimitadores de parámetros como '&', '?' en el caso de que pase contenido a una API.
Ben Lachman
'&' no es un carácter válido para el parámetro de cadena de consulta de URL. Intenta usar '& amp;' en lugar.
Nishant
1
stringByAddingPercentEscapesUsingEncoding es DEPRECATED "Use en su -stringByAddingPercentEncodingWithAllowedCharacters:lugar, que siempre usa la codificación UTF-8 recomendada, y que codifica para un componente o subcomponente de URL específico ya que cada componente o subcomponente de URL tiene diferentes reglas para qué caracteres son válidos".
Mihir Oza
89

Se han agregado nuevas API desde que se seleccionó la respuesta; Ahora puede usar NSURLUtilities. Dado que diferentes partes de las URL permiten diferentes caracteres, use el conjunto de caracteres correspondiente. El siguiente ejemplo codifica para su inclusión en la cadena de consulta:

encodedString = [myString stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];

Para convertir específicamente '&', deberá eliminarlo del conjunto de consultas de URL o usar un conjunto diferente, ya que '&' está permitido en una consulta de URL:

NSMutableCharacterSet *chars = NSCharacterSet.URLQueryAllowedCharacterSet.mutableCopy;
[chars removeCharactersInRange:NSMakeRange('&', 1)]; // %26
encodedString = [myString stringByAddingPercentEncodingWithAllowedCharacters:chars];
Peter DeWeese
fuente
1
Me tomó un tiempo entender qué querías decir con "usar NSURLUtilities", pero ahora veo que ese es el nombre de la categoría de NSString en la que Apple definió este nuevo método en iOS 7 y OS X 10.9.
Richard Venable
1
Aquí hay una respuesta más elaborada como esta: stackoverflow.com/a/20271177/456366
Richard Venable el
Sip. También figuraba como NSURLUtilities en la lista de nuevas API publicadas.
Peter DeWeese
'&', por supuesto, está permitido en una cadena de consulta, por lo que editaré mi respuesta usando el enlace de Richard.
Peter DeWeese
3
NSMakeRange('&', 1)no funciona en Swift, porque Swift no permite la conversión de caracteres a int sin hacks. Para usar esta solución en código Swift, use en removeCharactersInString("&")lugar de.removeCharactersInRange(...)
cbh2000
16

Ejemplo de Swift 2.0 (iOS 9 compatible)

extension String {

  func stringByURLEncoding() -> String? {

    let characters = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy() as! NSMutableCharacterSet

    characters.removeCharactersInString("&")

    guard let encodedString = self.stringByAddingPercentEncodingWithAllowedCharacters(characters) else {
      return nil
    }

    return encodedString

  }

}
Oliver Atkinson
fuente
2
@AlecThomas parece que tienes que saltar unos cuantos aros para llegar aquí, por eso es mejor esconderlo en una extensión
Oliver Atkinson
@OliverAtkinson - para mí Int (String (Character ("&"))) devuelve nil, como debería. En su lugar characters.removeCharactersInString ("&").
luz ambiental
14

actualización de ios 7

NSString *encode = [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];

NSString *decode = [encode stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
Underdog
fuente
Esto es inexacto. Querrá escapar de diferentes partes de la URL de manera diferente. stackoverflow.com/questions/3423545/…
Steve Moser
De hecho, depende de su servidor para los caracteres permitidos, aquí, en mi ejemplo en el servidor que estaba usando, solo se permite el conjunto de caracteres alfanuméricos, puede cambiarlo a caracteres específicos para satisfacer sus necesidades.
Underdog
1
La pregunta se refiere a la codificación del carácter '&'.
Steve Moser
8

Opté por usar la CFURLCreateStringByAddingPercentEscapesllamada según lo dado por la respuesta aceptada, sin embargo, en la versión más reciente de XCode (e IOS), resultó en un error, así que usé lo siguiente en su lugar:

NSString *apiKeyRaw = @"79b|7Qd.jW=])(fv|M&W0O|3CENnrbNh4}2E|-)J*BCjCMrWy%dSfGs#A6N38Fo~";

NSString *apiKey = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)apiKeyRaw, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8));
Mike Purcell
fuente
No está codificando espacio. El ejemplo @ "string string" y @ "string & string" se codifica correctamente. Por favor, hágame saber su opinión sobre el mismo.
Yogesh Lolusare
2
para mí la mayor parte del tiempo dedicado a la solución de este periodo fue la negación, donde me negaba a creer que el imposible de marco contiene algo llamado similar a 'urlencode' que hace esto sin jugar un poco ..
dancl
6

Intenta usar el stringByAddingPercentEncodingWithAllowedCharactersmétodo con [NSCharacterSet URLUserAllowedCharacterSet]él para cubrir todos los casos

C objetivo

NSString *value = @"Test / Test";
value = [value stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLUserAllowedCharacterSet]];

rápido

var value = "Test / Test"
value.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLUserAllowedCharacterSet())

Salida

Test%20%2F%20Test

Alex
fuente
6

Después de leer todas las respuestas para este tema y el ( incorrecto ) aceptado, quiero agregar mi contribución.

SI el objetivo es iOS7 +, y en 2017 debería hacerlo, ya que XCode hace que sea realmente difícil ofrecer compatibilidad con iOS8, la mejor manera, segura para subprocesos, rápida, y AMD tendrá soporte completo para UTF-8 para hacer esto es:

(Código del objetivo C)

@implementation NSString (NSString_urlencoding)

- (NSString *)urlencode {
    static NSMutableCharacterSet *chars = nil;
    static dispatch_once_t pred;

    if (chars)
        return [self stringByAddingPercentEncodingWithAllowedCharacters:chars];

    // to be thread safe
    dispatch_once(&pred, ^{
        chars = NSCharacterSet.URLQueryAllowedCharacterSet.mutableCopy;
        [chars removeCharactersInString:@"!*'();:@&=+$,/?%#[]"];
    });
    return [self stringByAddingPercentEncodingWithAllowedCharacters:chars];
}
@end

Esto extenderá NSString, excluirá los caracteres prohibidos RFC, admitirá caracteres UTF-8 y le permitirá usar cosas como:

NSString *myusername = "I'm[evil]&want(to)break!!!$->àéìòù";
NSLog(@"Source: %@ -> Dest: %@", myusername, [myusername urlencode]);

Eso se imprimirá en su consola de depuración:

Fuente: I'm [evil] & want (to) break !!! $ -> àéìòù -> Dest: I% 27m% 5Bevil% 5D% 26want% 28to% 29break% 21% 21% 21% 24-% 3E% C3 % A0% C3% A9% C3% AC% C3% B2% C3% B9

... tenga en cuenta también el uso de dispatch_once para evitar múltiples inicializaciones en entornos multiproceso.

gabry
fuente
5

Aquí hay un enfoque flexible listo para producción en Swift 5.x :

public extension CharacterSet {

    static let urlQueryParameterAllowed = CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "&?"))

    static let urlQueryDenied           = CharacterSet.urlQueryAllowed.inverted()
    static let urlQueryKeyValueDenied   = CharacterSet.urlQueryParameterAllowed.inverted()
    static let urlPathDenied            = CharacterSet.urlPathAllowed.inverted()
    static let urlFragmentDenied        = CharacterSet.urlFragmentAllowed.inverted()
    static let urlHostDenied            = CharacterSet.urlHostAllowed.inverted()

    static let urlDenied                = CharacterSet.urlQueryDenied
        .union(.urlQueryKeyValueDenied)
        .union(.urlPathDenied)
        .union(.urlFragmentDenied)
        .union(.urlHostDenied)


    func inverted() -> CharacterSet {
        var copy = self
        copy.invert()
        return copy
    }
}



public extension String {
    func urlEncoded(denying deniedCharacters: CharacterSet = .urlDenied) -> String? {
        return addingPercentEncoding(withAllowedCharacters: deniedCharacters.inverted())
    }
}

Ejemplo de uso:

print("Hello, World!".urlEncoded()!)
print("You&Me?".urlEncoded()!)
print("#Blessed 100%".urlEncoded()!)
print("Pride and Prejudice".urlEncoded(denying: .uppercaseLetters)!)

Salida:

Hello,%20World!
You%26Me%3F
%23Blessed%20100%25
%50ride and %50rejudice
Ben Leggiero
fuente
Incluya un comentario que explique el motivo de sus votos negativos para que sepa cómo escribir una mejor respuesta en el futuro 🙂
Ben Leggiero
4

Use NSURLComponents para codificar parámetros HTTP GET:

    var urlComponents = NSURLComponents(string: "https://www.google.de/maps/")!
    urlComponents.queryItems = [
        NSURLQueryItem(name: "q", value: String(51.500833)+","+String(-0.141944)),
        NSURLQueryItem(name: "z", value: String(6))
    ]
    urlComponents.URL     // returns https://www.google.de/maps/?q=51.500833,-0.141944&z=6

http://www.ralfebert.de/snippets/ios/encoding-nsurl-get-parameters/

Ralf Ebert
fuente
1
No codifica signos de unión, barras o punto y coma.
Fábio Oliveira
4

Este código me ayudó a codificar caracteres especiales

NSString* encPassword = [password stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]];
Rupesh Baldania
fuente
3

código rápido basado en la respuesta objc de chown en este hilo.

extension String {
    func urlEncode() -> String {
        return CFURLCreateStringByAddingPercentEscapes(
            nil,
            self,
            nil,
            "!*'();:@&=+$,/?%#[]",
            CFStringBuiltInEncodings.UTF8.rawValue
        )
    }
}
neoneye
fuente
44
esta función fue desaprobada en iOS 9 :(
Oliver Atkinson
3

En Swift 3 , prueba a continuación:

let stringURL = "YOUR URL TO BE ENCODE";
let encodedURLString = stringURL.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(encodedURLString)

Como, stringByAddingPercentEscapesUsingEncodingcodifica caracteres que no son URL pero deja los caracteres reservados (como !*'();:@&=+$,/?%#[]), puede codificar la URL como el siguiente código:

let stringURL = "YOUR URL TO BE ENCODE";
let characterSetTobeAllowed = (CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[] ").inverted)
if let encodedURLString = stringURL.addingPercentEncoding(withAllowedCharacters: characterSetTobeAllowed) {
    print(encodedURLString)
}
Dinesh
fuente
2

En Swift 3:

// exclude alpha and numeric == "full" encoding
stringUrl = stringUrl.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!;

// exclude hostname and symbols &,/ and etc
stringUrl = stringUrl.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!;
kg de mar
fuente
2

El consejo de Apple, en las notas de la versión 10.11, es:

Si necesita codificar en porcentaje una cadena URL completa, puede usar este código para codificar un NSString destinado a ser una URL (en urlStringToEncode):

NSString *percentEncodedURLString =
  [[NSURL URLWithDataRepresentation:[urlStringToEncode dataUsingEncoding:NSUTF8StringEncoding] relativeToURL:nil] relativeString];
nithinbhaktha
fuente
no parece funcionar, los caracteres como y todavía se quedan solos
kjyv
1

En mi caso donde el último componente eran letras árabes, hice lo siguiente en Swift 2.2:

extension String {

 func encodeUTF8() -> String? {

    //If I can create an NSURL out of the string nothing is wrong with it
    if let _ = NSURL(string: self) {

        return self
    }

    //Get the last component from the string this will return subSequence
    let optionalLastComponent = self.characters.split { $0 == "/" }.last


    if let lastComponent = optionalLastComponent {

        //Get the string from the sub sequence by mapping the characters to [String] then reduce the array to String
        let lastComponentAsString = lastComponent.map { String($0) }.reduce("", combine: +)


        //Get the range of the last component
        if let rangeOfLastComponent = self.rangeOfString(lastComponentAsString) {
            //Get the string without its last component
            let stringWithoutLastComponent = self.substringToIndex(rangeOfLastComponent.startIndex)


            //Encode the last component
            if let lastComponentEncoded = lastComponentAsString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.alphanumericCharacterSet()) {


            //Finally append the original string (without its last component) to the encoded part (encoded last component)
            let encodedString = stringWithoutLastComponent + lastComponentEncoded

                //Return the string (original string/encoded string)
                return encodedString
            }
        }
    }

    return nil;
}
}

uso:

let stringURL = "http://xxx.dev.com/endpoint/nonLatinCharacters"

if let encodedStringURL = stringURL.encodeUTF8() {

    if let url = NSURL(string: encodedStringURL) {

      ...
    }

} 
Bobj-C
fuente
1
-(NSString *)encodeUrlString:(NSString *)string {
  return CFBridgingRelease(
                    CFURLCreateStringByAddingPercentEscapes(
                        kCFAllocatorDefault,
                        (__bridge CFStringRef)string,
                        NULL,
                        CFSTR("!*'();:@&=+$,/?%#[]"),
                        kCFStringEncodingUTF8)
                    );
}

de acuerdo al siguiente blog

Anton Kashpor
fuente
Pero está en desuso en iOS 9. ¿Cuál es el código actualizado para esto?
Sujit Nachan
1

Muchas respuestas pero no me funcionaron, así que intenté lo siguiente:

fun simpleServiceCall(for serviceUrl: String, appendToUrl: String) { 
    let urlString: String = serviceUrl + appendToUrl.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!     

    let finalUrl = URL(string: urlString)!

    //continue to execute your service call... 
}

Esperemos que ayude a alguien. Gracias

Sanjeevcn
fuente
0

Para los parámetros de consulta codificados en formularios www individuales, hice una categoría en NSString:

- (NSString*)WWWFormEncoded{
     NSMutableCharacterSet *chars = NSCharacterSet.alphanumericCharacterSet.mutableCopy;
     [chars addCharactersInString:@" "];
     NSString* encodedString = [self stringByAddingPercentEncodingWithAllowedCharacters:chars];
     encodedString = [encodedString stringByReplacingOccurrencesOfString:@" " withString:@"+"];
     return encodedString;
}
Pete Kamm
fuente
0

// Esto es sin prueba

NSMutableCharacterSet* set = [[NSCharacterSet alphanumericCharacterSet] mutableCopy];
[set addCharactersInString:@"-_.~"];
NSString *encode = [test stringByAddingPercentEncodingWithAllowedCharacters:set];
Achilles Wang
fuente
0

Me enfrenté a un problema similar al pasar cadenas complejas como un parámetro POST. Mis cadenas pueden contener caracteres asiáticos, espacios, comillas y todo tipo de caracteres especiales. La solución que finalmente encontré fue convertir mi cadena en la serie de Unicodes correspondiente, por ejemplo, "Hu0040Hu0020Hu03f5 ...." usando [NSString stringWithFormat: @ "Hu% 04x", [string characterAtIndex: i]] para obtener el Unicode de cada carácter en la cadena original. Lo mismo se puede hacer en Java.

Esta cadena se puede pasar de forma segura como un parámetro POST.

En el lado del servidor (PHP), cambio toda la "H" a "\" y paso la cadena resultante a json_decode. El último paso es escapar de las comillas simples antes de almacenar la cadena en MySQL.

De esta manera puedo almacenar cualquier cadena UTF8 en mi servidor.

Georges
fuente
0

Este me está funcionando.

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
}

Encontré la función anterior en este enlace: http://useyourloaf.com/blog/how-to-percent-encode-a-url-string/

También puede usar esta función con extensión rápida. Avísame si hay algún problema.

Gaurav Singla
fuente