Objective-c iPhone Prozent codieren eine Zeichenfolge?

72

Ich möchte die prozentual codierte Zeichenfolge für diese spezifischen Buchstaben erhalten. Wie geht das in Ziel-C?

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

Prozentkodierendes Wiki

Bitte testen Sie mit dieser Zeichenfolge und prüfen Sie, ob sie funktioniert:

myURL = @"someurl/somecontent"

Ich möchte, dass die Zeichenfolge wie folgt aussieht:

myEncodedURL = @"someurl%2Fsomecontent"

Ich habe es schon mit versucht, stringByAddingPercentEscapesUsingEncoding: NSASCIIStringEncodingaber es funktioniert nicht, das Ergebnis ist immer noch das gleiche wie die ursprüngliche Zeichenfolge. Bitte beraten.

Hoang Pham
quelle
Sie sollten% zu dieser Liste hinzufügen.
JeremyP
Ich verstehe Ihren Kommentar nicht: -s
Hoang Pham
Er meint, dass das %Zeichen selbst URL-codiert sein sollte. Auf diese Weise wird ein String - Eingang wie %2Fverwandelt sich in %252F. Ich würde auch vorschlagen, das Leerzeichen ( %20) zu dieser Liste hinzuzufügen .
David

Antworten:

143

Ich habe festgestellt, dass beide stringByAddingPercentEscapesUsingEncoding:und CFURLCreateStringByAddingPercentEscapes()unzureichend sind. Bei der NSStringMethode fehlen einige Zeichen, und mit der CF-Funktion können Sie nur angeben, welchen (bestimmten) Zeichen Sie entkommen möchten. Die richtige Spezifikation besteht darin, alle Zeichen außer einem kleinen Satz zu maskieren.

Um dies zu beheben, habe ich eine NSStringKategoriemethode erstellt, um eine Zeichenfolge ordnungsgemäß zu codieren. Es codiert prozentual alles AUSSER [a-zA-Z0-9.-_~]und codiert auch Leerzeichen als +(gemäß dieser Spezifikation ). Die Codierung von Unicode-Zeichen wird ebenfalls ordnungsgemäß verarbeitet.

- (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;
}
Dave DeLong
quelle
7
Beachten Sie, dass die Codierung von Leerzeichen als + statt% 20 gemäß x-www-form-urlencoded erfolgt und nicht gemäß oauth-1.3.6 (dem von Ihnen geposteten Spezifikationslink).
Jano
6
@mihir natürlich. Dies ist nur eine Konvertierungsmethode. Wenn Sie nicht möchten, dass eine ganze Zeichenfolge codiert wird, geben Sie nicht die gesamte Zeichenfolge weiter ...
Dave DeLong
3
Sie haben verstanden, eigentlich habe ich alles implementiert und als dieses Problem zu realisieren, müssen Sie jetzt jeden Parameter einzeln codieren ... sehr umständlich ... :(
Mihir Mehta
Wie @Jano hervorhebt, sind + Codierungsräume hier nicht ideal und für den Abfrageteil der URL reserviert. Siehe stackoverflow.com/questions/2678551/…
Levigroker
@zaitsman Die Lizenz für alle Inhalte auf dieser Website befindet sich in der Fußzeile: cc-wiki mit Namensnennung erforderlich.
Dave DeLong
105

Das iOS 7 SDK bietet jetzt eine bessere Alternative stringByAddingPercentEscapesUsingEncoding, mit der Sie festlegen können, dass alle Zeichen außer bestimmten zulässigen Zeichen maskiert werden sollen. Es funktioniert gut, wenn Sie die URL in Teilen aufbauen:

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];

Obwohl es seltener vorkommt, dass die anderen Teile der URL Variablen sind, gibt es in der Kategorie NSURLUtilities auch Konstanten für diese:

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

[NSCharacterSet URLQueryAllowedCharacterSet]Enthält alle Zeichen, die im Abfrageteil der URL zulässig sind (der Teil, der mit dem ?und vor dem #für ein Fragment beginnt , falls vorhanden), einschließlich der Zeichen ?und &oder =, die zur Begrenzung der Parameternamen und -werte verwendet werden. Bei Abfrageparametern mit alphanumerischen Werten kann eines dieser Zeichen in den Werten der Variablen enthalten sein, die zum Erstellen der Abfragezeichenfolge verwendet werden. In diesem Fall muss jeder Teil der Abfragezeichenfolge maskiert werden, was nur ein wenig mehr Arbeit erfordert:

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];

Das unescapedValuekönnte sogar eine ganze URL, wie für einen Rückruf oder Umleitung sein:

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

Hinweis: Nicht NSURL initWithScheme:(NSString *)scheme host:(NSString *)host path:(NSString *)pathfür eine URL mit einer Abfragezeichenfolge verwenden, da dadurch mehr prozentuale Escapezeichen zum Pfad hinzugefügt werden.

Rob bei TVSeries.com
quelle
Dies sollte die führende Antwort sein. Es ist mit den iOS7-Dienstprogrammen auf dem neuesten Stand und weist korrekt darauf hin, dass verschiedene Teile der URL unterschiedlich maskiert werden sollten.
Algen
1
Da nur das '+' fehlt, möchten Sie vielleicht etwas hinzufügen, [URLQueryPartAllowedCharacterSet removeCharactersInRange:NSMakeRange('+', 1)];aber ansonsten ist dieser Code perfekt, um Zeichenfolgen zu entkommen. Danke!
Quentin G.
9
Es gibt eine weniger wortreiche Alternative: URLQueryPartAllowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; [URLQueryPartAllowedCharacterSet removeCharactersInString:@"?&=@+/'"];Ich habe auch 100.000 Iterationen auf einem Mac getestet und festgestellt, dass dies durchweg etwas schneller ist als ein removeCharactersInRange:mehrmaliger Aufruf .
Robotspacer
wie wäre es mit dem %? sollte es nicht auch codiert werden, wenn es im Wert ist?
NJZK2
5
NSString *encodedString = [myString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];

Es wird Ihre Inline-Zeichenfolge nicht ersetzen. Es wird eine neue Zeichenfolge zurückgegeben. Dies wird durch die Tatsache impliziert, dass die Methode mit dem Wort "string" beginnt. Es ist eine bequeme Methode, eine neue Instanz von NSString basierend auf dem aktuellen NSString zu instanziieren.

Hinweis - Die neue Zeichenfolge wird autorelease"d" sein. Rufen Sie also nicht "release" auf, wenn Sie damit fertig sind.

Dan Ray
quelle
Danke, aber es funktioniert nicht, bitte überprüfen Sie meine aktualisierte Frage.
Hoang Pham
2
Das Ergebnis ist immer noch das gleiche, es gibt keine Änderung.
Hoang Pham
5

StringByAddingPercentEscapesUsingEncoding von NSString: sieht aus wie das, wonach Sie suchen .

BEARBEITEN : Hier ist ein Beispiel, das CFURLCreateStringByAddingPercentEscapesstattdessen verwendet wird. originalStringkann entweder ein NSStringoder ein sein CFStringRef.

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

Bitte beachten Sie, dass dies nicht getestet wurde. Sie sollten sich die Dokumentationsseite ansehen , um sicherzustellen, dass Sie die Semantik der Speicherzuweisung für CFStringRef, die Idee der gebührenfreien Überbrückung usw. verstehen .

Außerdem weiß ich nicht (auf Anhieb), welches der im legalURLCharactersToBeEscapedArgument angegebenen Zeichen ohnehin maskiert worden wäre (da es in URLs illegal ist). Möglicherweise möchten Sie dies überprüfen, obwohl es vielleicht besser ist, nur auf der sicheren Seite zu sein und direkt die Zeichen anzugeben, die maskiert werden sollen.

Ich mache diese Antwort zu einem Community-Wiki, damit Leute mit mehr Wissen über CoreFoundation Verbesserungen vornehmen können.

David
quelle
Welche NSStringEcoding muss ich verwenden, damit alle oben genannten Zeichen korrekt funktionieren? Hast du es mit einer Schnur versucht?
Hoang Pham
Hmm, es scheint keinen NSStringEncodingWert für das zu geben, was Sie wollen. Sie können stattdessen versuchen CFURLCreateStringByAddingPercentEscapes( developer.apple.com/mac/library/documentation/CoreFoundation/… ) - Sie können damit die zu entkommenden Zeichen direkt angeben.
David
... oh, und übrigens: NSStringund CFStringRefsind "gebührenfrei überbrückt", was bedeutet, dass sie austauschbar an die Funktionen des anderen weitergegeben werden können.
David
4

Nach dem RFC3986-Standard verwende ich Folgendes zum Codieren von URL-Komponenten:

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

Ausgabe: email%2Bwith%2Bplus%40example.com

Eneko Alonso
quelle
HI, Alonso, ich habe gerade eine E-Mail-Adresse verschlüsselt. Wie kann ich es tun? Meine E-Mail-Adresse lautet beispielsweise [email protected]. Wenn ich @ codiere, meldet unser Server einen Fehler. Kannst du mir dabei helfen? Danke
mmm2006
2

Wenn Sie die ASI HttpRequest-Bibliothek in Ihrem Objective-C-Programm verwenden, was ich nicht genug empfehlen kann, können Sie die Helfer-API "encodeURL" für das ASIFormDataRequest-Objekt verwenden. Leider ist die API nicht statisch, daher lohnt es sich möglicherweise, eine Erweiterung mithilfe ihrer Implementierung in Ihrem Projekt zu erstellen.

Der Code, der direkt aus ASIFormDataRequest.m für die Implementierung von encodeURL kopiert wurde, lautet:

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

Wie Sie sehen können, handelt es sich im Wesentlichen um einen Wrapper CFURLCreateStringByAddingPercentEscapes, der sich um alle Zeichen kümmert, die ordnungsgemäß maskiert werden sollten.

Bhavinb
quelle
0

Bevor ich Robs Antwort bemerkte, die gut zu funktionieren scheint und als sauberer bevorzugt wird, portierte ich Daves Antwort auf Swift. Ich lasse es hier, falls jemand interessiert ist:

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
    }
}
Ben Baron
quelle
1
Zum Beispiel:let string = "te&st"; print(string.URLEncodedValue)
Ben Baron
0

In Swift4:

 var str = "someurl/somecontent"

 let percentEncodedString = str.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
Ankit garg
quelle