Como codifico uma string em URL

Respostas:

291

Infelizmente, stringByAddingPercentEscapesUsingEncodingnem sempre funciona 100%. Ele codifica caracteres que não são de URL, mas deixa os caracteres reservados (como barra /ee comercial &) sozinhos. Aparentemente, este é um bug que a Apple sabe, mas como eles ainda não o corrigiram, eu tenho usado essa categoria para codificar url uma string:

@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 assim:

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

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

Isso também funciona:

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

Algumas boas leituras sobre o assunto:

O objetivo-c por cento do iPhone codifica uma string?
Codificação de URL Objective-C e Swift

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
fonte
12
Por que diabos você usaria uma categoria complicada como essa, em vez de simplesmente ir para CFURLCreateStringByAddingPercentEscapes () , onde você pode especificar explicitamente quais caracteres deseja sempre escapar.
Lily Ballard
8
@KevinBallard porque a função CF funciona em um modelo inclusivo ( "escapar esses personagens"), e, geralmente, você quer trabalhar em um modelo exclusivo ( "fuga de tudo , exceto esses personagens")
Dave DeLong
31
@DaveDeLong É aí que eu consegui. Tem sido uma categoria padrão em todos os meus projetos há um ano ou mais, então eu imagino que você possa ter sido a fonte original! Eu editei o texto para que não pareça que estou tentando receber crédito por escrevê-lo).
Chown
4
Por quê? if (thisChar == '') {[output appendString: @ "+"]; } Com esta se ele vai codificar espaços com% 20 como seria de esperar
Chris Stephens
127

Isso pode ser útil

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

Para iOS 7 ou superior, a maneira recomendada é:

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

Você pode escolher o conjunto de caracteres permitido conforme o requisito do componente de URL.

Nishant
fonte
7
Isso também não codifica corretamente delimitadores de parâmetros como '&', '?' no caso em que você está transmitindo conteúdo para uma API.
Ben Lachman
'&' não é um caractere válido para o parâmetro da string de consulta da URL. Por favor, tente usar '& amp;' em vez de.
Nishant
1
stringByAddingPercentEscapesUsingEncoding DEPRECATED "Use -stringByAddingPercentEncodingWithAllowedCharacters:, em vez disso, que sempre usa a codificação UTF-8 recomendada e que codifica para um componente ou subcomponente específico da URL, pois cada componente ou subcomponente da URL tem regras diferentes para quais caracteres são válidos."
Mihir Oza
89

Novas APIs foram adicionadas desde que a resposta foi selecionada; Agora você pode usar NSURLUtilities. Como partes diferentes de URLs permitem caracteres diferentes, use o conjunto de caracteres aplicável. O exemplo a seguir codifica para inclusão na cadeia de consulta:

encodedString = [myString stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];

Para converter especificamente '&', você precisará removê-lo do conjunto de consultas de URL ou usar um conjunto diferente, pois '&' é permitido em uma consulta de URL:

NSMutableCharacterSet *chars = NSCharacterSet.URLQueryAllowedCharacterSet.mutableCopy;
[chars removeCharactersInRange:NSMakeRange('&', 1)]; // %26
encodedString = [myString stringByAddingPercentEncodingWithAllowedCharacters:chars];
Peter DeWeese
fonte
1
Demorei um pouco para entender o que você quis dizer com "use NSURLUtilities" - mas agora vejo que esse é o nome da categoria de NSString na qual a Apple definiu esse novo método no iOS 7 e no OS X 10.9.
Richard Venable 21/03
1
Há uma resposta mais elaborada como este aqui: stackoverflow.com/a/20271177/456366
Richard Venable
Sim. Também foi listado como NSURLUtilities na lista de novas APIs lançadas.
Peter DeWeese
É claro que '&' é permitido em uma string de consulta, então eu editarei minha resposta usando o link de Richard.
Peter DeWeese
3
NSMakeRange('&', 1)não funciona no Swift, porque o Swift não permite a conversão de caracteres para int sem hacks. Para utilizar esta solução em código Swift, use removeCharactersInString("&")em vez de.removeCharactersInRange(...)
cbh2000
16

Exemplo do Swift 2.0 (compatível com iOS 9)

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
fonte
2
@AlecThomas parece que você tem que saltar através de algumas aros para chegar aqui, é por isso que o seu melhor para escondê-lo em uma extensão
Oliver Atkinson
@OliverAtkinson - para mim Int (String (Character ("&"))) retorna nulo, como deveria. Em vez disso, characters.removeCharactersInString ("&").
ambientlight
14

atualização ios 7

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

NSString *decode = [encode stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
Underdog
fonte
Isso é impreciso. Você deseja escapar de diferentes partes do URL de maneira diferente. stackoverflow.com/questions/3423545/…
Steve Moser
Na verdade, depende do servidor para caracteres permitidos, aqui no meu exemplo no servidor em que eu estava usando apenas o conjunto de caracteres alfanuméricos permitidos, você pode alterá-lo para caracteres específicos para atender às suas necessidades.
Underdog
1
A pergunta é feita sobre a codificação do caractere '&'.
Steve Moser
8

Optei por usar a CFURLCreateStringByAddingPercentEscapeschamada como dada pela resposta aceita, no entanto, na versão mais recente do XCode (e IOS), resultou em um erro, então usei o seguinte:

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
fonte
Não está codificando espaço. Exemplo @ "string string" e @ "string & string" estão codificados corretamente. Por favor, deixe-me saber sua opinião sobre o mesmo.
Yogesh Lolusare
2
para mim a maior parte do tempo gasto resolver este foi o período de negação, onde eu me recusei a acreditar que o does not quadro conter algo chamado semelhante ao 'urlencode' que faz isso sem a andar ..
dancl
6

Tente usar o stringByAddingPercentEncodingWithAllowedCharactersmétodo com[NSCharacterSet URLUserAllowedCharacterSet] ele irá cobrir todos os casos

Objetivo C

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

rápido

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

Resultado

Test%20%2F%20Test

Alex
fonte
6

Depois de ler todas as respostas para este tópico e a ( incorreta ) aceita, quero adicionar minha contribuição.

E SE o objetivo for iOS7 +, e em 2017, como o XCode dificulta a compatibilidade com iOS8, a melhor maneira, thread safe, fast, amd terá suporte completo a UTF-8 para fazer isso:

(Código do 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

Isso estenderá o NSString, excluirá caracteres proibidos RFC, suportará caracteres UTF-8 e permitirá que você use coisas como:

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

Isso será impresso no seu console de depuração:

Fonte: Eu sou [mau] e quero quebrar !!! $ -> àéìòù -> Dest: I% 27m% 5Bevil% 5D% 26want% 28to% 29break% 21% 21% 21% 24-% 3E% C3 % A0% C3% A9% C3% AC% C3% B2% C3% B9

... observe também o uso de dispatch_once para evitar várias inicializações em ambientes multithread.

gabry
fonte
5

Aqui está uma abordagem flexível pronta para produção no 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())
    }
}

Exemplo de uso:

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

Resultado:

Hello,%20World!
You%26Me%3F
%23Blessed%20100%25
%50ride and %50rejudice
Ben Leggiero
fonte
Por favor, inclua um comentário explicando o motivo da sua downvotes então eu sei como escrever uma resposta melhor no futuro 🙂
Ben leggiero
4

Use NSURLComponents para codificar os 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
fonte
1
Não codifica e comercial, barras ou ponto e vírgula.
Fábio Oliveira
4

Esse código me ajudou a codificar caracteres especiais

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

código swift baseado na resposta objc do chown neste tópico.

extension String {
    func urlEncode() -> String {
        return CFURLCreateStringByAddingPercentEscapes(
            nil,
            self,
            nil,
            "!*'();:@&=+$,/?%#[]",
            CFStringBuiltInEncodings.UTF8.rawValue
        )
    }
}
neoneye
fonte
4
esta função foi descontinuada no iOS 9 :(
Oliver Atkinson
3

No Swift 3 , tente abaixo:

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

Como stringByAddingPercentEscapesUsingEncodingcodifica caracteres que não são de URL, mas deixa os caracteres reservados (como !*'();:@&=+$,/?%#[]), você pode codificar o URL como o seguinte código:

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

No rápido 3:

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

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

O conselho da Apple, nas notas de versão 10.11, é:

Se você precisar codificar em porcentagem uma cadeia de URL inteira, poderá usar esse código para codificar uma NSString destinada a ser uma URL (em urlStringToEncode):

NSString *percentEncodedURLString =
  [[NSURL URLWithDataRepresentation:[urlStringToEncode dataUsingEncoding:NSUTF8StringEncoding] relativeToURL:nil] relativeString];
nithinbhaktha
fonte
não parece trabalho, caracteres como & ainda são deixados sozinhos
kjyv
1

No meu caso, onde o último componente era letras árabes, fiz o seguinte em 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
fonte
1
-(NSString *)encodeUrlString:(NSString *)string {
  return CFBridgingRelease(
                    CFURLCreateStringByAddingPercentEscapes(
                        kCFAllocatorDefault,
                        (__bridge CFStringRef)string,
                        NULL,
                        CFSTR("!*'();:@&=+$,/?%#[]"),
                        kCFStringEncodingUTF8)
                    );
}

de acordo com o seguinte blog

Anton Kashpor
fonte
Mas está obsoleto no iOS 9. Qual é o código atualizado para isso?
Sujit Nachan
1

Tantas respostas, mas não funcionaram para mim, então tentei o seguinte:

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... 
}

Espero que ajude alguém. obrigado

Sanjeevcn
fonte
0

Para parâmetros de consulta individuais codificados em www, criei uma categoria no NSString:

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

// Isso é sem teste

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

Eu enfrentei um problema semelhante ao passar seqüências complexas como um parâmetro POST. Minhas strings podem conter caracteres asiáticos, espaços, aspas e todos os tipos de caracteres especiais. A solução que encontrei foi converter minha string na série correspondente de unicodes, por exemplo, "Hu0040Hu0020Hu03f5 ...." usando [NSString stringWithFormat: @ "Hu% 04x", [string characterAtIndex: i]] para obter o Unicode de cada caractere na sequência original. O mesmo pode ser feito em Java.

Essa sequência pode ser transmitida com segurança como um parâmetro POST.

No lado do servidor (PHP), altero todo o "H" para "\" e passo a sequência resultante para json_decode. O passo final é escapar aspas simples antes de armazenar a string no MySQL.

Dessa forma, posso armazenar qualquer string UTF8 no meu servidor.

Georges
fonte
0

Este está trabalhando para mim.

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
}

Encontrei a função acima neste link: http://useyourloaf.com/blog/how-to-percent-encode-a-url-string/

Você também pode usar esta função com extensão rápida. Entre em contato se houver algum problema.

Gaurav Singla
fonte