Làm cách nào để URL mã hóa một chuỗi


Câu trả lời:


291

Thật không may, stringByAddingPercentEscapesUsingEncodingkhông phải lúc nào cũng hoạt động 100%. Nó mã hóa các ký tự không phải URL nhưng chỉ để lại các ký tự dành riêng (như dấu gạch chéo /và ký hiệu &). Rõ ràng đây là một lỗi mà Apple nhận thấy, nhưng vì họ chưa sửa nó, tôi đã sử dụng danh mục này để mã hóa url một chuỗi:

@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;
}

Được sử dụng như thế này:

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

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

Điều này cũng hoạt động:

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

Một số đọc tốt về chủ đề này:

Phần trăm iPhone khách quan mã hóa một chuỗi?
Mã hóa URL Objective-C và 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 /


12
Tại sao bạn lại sử dụng một danh mục phức tạp như thế này thay vì chỉ thả xuống CFURLCreateStringByAddingPercentEscapes () , nơi bạn có thể chỉ định rõ ràng những ký tự bạn muốn luôn thoát.
Lily Ballard

8
@KevinBallard vì chức năng CF hoạt động trên một mô hình bao gồm ("thoát các ký tự này") và thông thường bạn muốn làm việc trên một mô hình độc quyền ("thoát mọi thứ trừ các ký tự này")
Dave DeLong 11/11/11


31
@DaveDeLong Đó có thể là nơi tôi đã nhận nó. Đây là một danh mục tiêu chuẩn trong tất cả các dự án của tôi trong một năm hoặc lâu hơn, vì vậy tôi tưởng tượng bạn có thể là nguồn ban đầu! Tôi đã chỉnh sửa từ ngữ để có vẻ như tôi đang cố lấy tín dụng để viết nó).
chown

4
Tại sao? if (thisChar == '') {[đầu ra appendString: @ "+"]; } Nếu không, nó sẽ mã hóa khoảng trắng với% 20 như tôi mong đợi
Chris Stephens

127

Điều này có thể hữu ích

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

Đối với iOS 7+, cách được đề xuất là:

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

Bạn có thể chọn bộ ký tự được phép theo yêu cầu của thành phần URL.


7
Điều này cũng không mã hóa chính xác các dấu phân cách tham số như '&', '?' trong trường hợp bạn chuyển nội dung vào API.
Ben Lachman

'&' không phải là ký tự hợp lệ cho tham số chuỗi truy vấn URL. Vui lòng thử sử dụng '& amp;' thay thế.
Nishant

1
stringByAddingPercentEscapesUsingEncoding là DEPRECATED " -stringByAddingPercentEncodingWithAllowedCharacters:Thay vào đó, sử dụng luôn mã hóa UTF-8 được đề xuất và mã hóa cho một thành phần hoặc thành phần URL cụ thể do mỗi thành phần hoặc thành phần URL có các quy tắc khác nhau cho các ký tự nào là hợp lệ."
Mihir Oza

89

API mới đã được thêm vào kể từ khi câu trả lời được chọn; Bây giờ bạn có thể sử dụng NSURLUtilities. Vì các phần khác nhau của URL cho phép các ký tự khác nhau, hãy sử dụng bộ ký tự áp dụng. Ví dụ sau mã hóa để đưa vào chuỗi truy vấn:

encodedString = [myString stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];

Để chuyển đổi cụ thể '&', bạn sẽ cần xóa nó khỏi bộ truy vấn url hoặc sử dụng một bộ khác, vì '&' được cho phép trong truy vấn URL:

NSMutableCharacterSet *chars = NSCharacterSet.URLQueryAllowedCharacterSet.mutableCopy;
[chars removeCharactersInRange:NSMakeRange('&', 1)]; // %26
encodedString = [myString stringByAddingPercentEncodingWithAllowedCharacters:chars];

1
Phải mất một thời gian tôi mới hiểu được ý của bạn khi "sử dụng NSURLUtilities" - nhưng bây giờ tôi thấy đó là tên của danh mục NSString trong đó Apple định nghĩa phương pháp mới này trong iOS 7 và OS X 10.9.
Richard Venable

1
Có một câu trả lời phức tạp hơn như thế này ở đây: stackoverflow.com/a/20271177/456366
Richard Venable

Vâng Nó cũng được liệt kê là NSURLUtilities trong danh sách các API mới được phát hành.
Peter De Weese

'&' Tất nhiên được cho phép trong chuỗi truy vấn, vì vậy tôi sẽ chỉnh sửa câu trả lời của mình bằng liên kết của Richard.
Peter De Weese 18/07 '

3
NSMakeRange('&', 1)không hoạt động trong Swift, vì Swift không cho phép char chuyển đổi int mà không bị hack. Để sử dụng giải pháp này trong mã Swift, hãy sử dụng removeCharactersInString("&")thay vì.removeCharactersInRange(...)
cbh2000

16

Ví dụ Swift 2.0 (Tương thích 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

  }

}

2
@AlecThomas bạn dường như phải nhảy qua một vài vòng để đến đây, đó là lý do tại sao nên giấu nó đi trong một phần mở rộng
Oliver Atkinson

@OliverAtkinson - đối với tôi Int (Chuỗi (Ký tự ("&"))) trả về nil, như bình thường. Thay vào đó là character.removeChar characterInString ("&").
ambientlight

14

Cập nhật ios 7

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

NSString *decode = [encode stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

Điều này là không chính xác. Bạn sẽ muốn thoát các phần khác nhau của URL khác nhau. stackoverflow.com/questions/3423545/ Mạnh
Steve Moser

Nó thực sự phụ thuộc vào máy chủ của bạn cho các ký tự được phép, ở đây trên ví dụ của tôi trong máy chủ tôi đang sử dụng nó chỉ cho phép bộ ký tự chữ và số, bạn có thể thay đổi nó thành các ký tự cụ thể để phục vụ nhu cầu của bạn.
Underdog

1
Câu hỏi hỏi về việc mã hóa ký tự '&'.
Steve Moser

8

Tôi đã chọn sử dụng CFURLCreateStringByAddingPercentEscapescuộc gọi như được đưa ra bởi câu trả lời được chấp nhận, tuy nhiên trong phiên bản mới nhất của XCode (và IOS), nó đã gây ra lỗi, vì vậy đã sử dụng cách sau:

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

Nó không phải là không gian mã hóa. Ví dụ @ "chuỗi chuỗi" và @ "chuỗi & chuỗi" được mã hóa đúng. Xin vui lòng cho tôi biết đầu vào của bạn trên cùng.
Yogesh Lolusare

2
Đối với tôi, phần lớn thời gian dành cho việc giải quyết đây là khoảng thời gian từ chối mà tôi từ chối tin rằng khung công tác không chứa thứ gì đó có tên tương tự như 'urlencode', điều này không gây ra sự lộn xộn ..
dancl

6

Cố gắng sử dụng stringByAddingPercentEncodingWithAllowedCharactersphương pháp với [NSCharacterSet URLUserAllowedCharacterSet]nó sẽ bao gồm tất cả các trường hợp

Mục tiêu C

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

nhanh

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

Đầu ra

Test%20%2F%20Test


6

Sau khi đọc tất cả các câu trả lời cho chủ đề này và ( sai ) được chấp nhận, tôi muốn thêm đóng góp của mình.

NẾU mục tiêu là iOS7 + và trong năm 2017, vì XCode thực sự khó cung cấp khả năng tương thích trong iOS8, cách tốt nhất, an toàn, nhanh, amd sẽ hỗ trợ UTF-8 đầy đủ để thực hiện điều này là:

(Mã C khách quan)

@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

Điều này sẽ mở rộng NSString, sẽ loại trừ các ký tự bị cấm RFC, hỗ trợ các ký tự UTF-8 và cho phép bạn sử dụng những thứ như:

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

Điều đó sẽ in trên bảng điều khiển gỡ lỗi của bạn:

Nguồn: Tôi [ác] & muốn (phá vỡ) !!! $ -> àéìòù -> Dest: Tôi% 27m% 5Bevil% 5D% 26want% 28to% 29break% 21% 21% 21% 24-% 3E% C3 % A0% C3% A9% C3% AC% C3% B2% C3% B9

... cũng lưu ý việc sử dụng Clark_once để tránh nhiều lần khởi tạo trong môi trường đa luồng.


5

Đây là một cách tiếp cận linh hoạt sẵn sàng sản xuất trong 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())
    }
}

Ví dụ sử dụng:

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

Đầu ra:

Hello,%20World!
You%26Me%3F
%23Blessed%20100%25
%50ride and %50rejudice

Vui lòng bao gồm một nhận xét giải thích lý do cho các downvote của bạn để tôi biết cách viết câu trả lời tốt hơn trong tương lai
Ben Leggiero

4

Sử dụng NSURLComponents để mã hóa các tham số 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-parameter/


1
Nó không mã hóa ký hiệu, dấu gạch chéo hoặc dấu chấm phẩy.
Fábio Oliveira

4

Mã này đã giúp tôi mã hóa các ký tự đặc biệt

NSString* encPassword = [password stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]];

3

mã nhanh chóng dựa trên câu trả lời objc của chown trong chủ đề này.

extension String {
    func urlEncode() -> String {
        return CFURLCreateStringByAddingPercentEscapes(
            nil,
            self,
            nil,
            "!*'();:@&=+$,/?%#[]",
            CFStringBuiltInEncodings.UTF8.rawValue
        )
    }
}

4
chức năng này không được dùng nữa trong iOS 9 :(
Oliver Atkinson

3

Trong Swift 3 , vui lòng thử bên dưới:

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

Vì, stringByAddingPercentEscapesUsingEncodingmã hóa các ký tự không phải URL nhưng để lại các ký tự dành riêng (như !*'();:@&=+$,/?%#[]), Bạn có thể mã hóa url như mã sau:

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

2

Trong 3 nhanh chóng:

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

// exclude hostname and symbols &,/ and etc
stringUrl = stringUrl.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!;

2

Lời khuyên của Apple, trong ghi chú phát hành 10.11, là:

Nếu bạn cần mã hóa phần trăm toàn bộ chuỗi URL, bạn có thể sử dụng mã này để mã hóa NSString dự định là một URL (trong urlStringToEncode):

NSString *percentEncodedURLString =
  [[NSURL URLWithDataRepresentation:[urlStringToEncode dataUsingEncoding:NSUTF8StringEncoding] relativeToURL:nil] relativeString];

dường như không hoạt động, những ký tự như & vẫn còn một mình
kjyv

1

Trong trường hợp của tôi, thành phần cuối cùng là các chữ cái Ả Rập, tôi đã làm như sau 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;
}
}

sử dụng:

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

if let encodedStringURL = stringURL.encodeUTF8() {

    if let url = NSURL(string: encodedStringURL) {

      ...
    }

} 

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

theo blog sau


Nhưng nó không được dùng trong iOS 9. Mã cập nhật cho cái này là gì?
Sujit Nachan

1

Rất nhiều câu trả lời nhưng không hiệu quả với tôi, vì vậy tôi đã thử như sau:

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

Hy vọng nó sẽ giúp được ai đó. cảm ơn


0

Đối với các tham số truy vấn được mã hóa biểu mẫu www riêng lẻ, tôi đã tạo một danh mục trên NSString:

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

0

// Đây là không có kiểm tra

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

0

Tôi gặp phải một vấn đề tương tự khi truyền các chuỗi phức tạp dưới dạng tham số POST. Chuỗi của tôi có thể chứa các ký tự châu Á, dấu cách, dấu ngoặc kép và tất cả các loại ký tự đặc biệt. Giải pháp cuối cùng tôi tìm thấy là chuyển đổi chuỗi của tôi thành chuỗi unicodes phù hợp, ví dụ: "Hu0040Hu0020Hu03f5 ...." bằng cách sử dụng [NSString chuỗiWithFormat: @ "Hu% 04x", [chuỗi ký tựAt Index: i]] để lấy Unicode từ mỗi ký tự trong chuỗi gốc. Điều tương tự có thể được thực hiện trong Java.

Chuỗi này có thể được truyền an toàn dưới dạng tham số POST.

Về phía máy chủ (PHP), tôi thay đổi tất cả "H" thành "\" và tôi chuyển chuỗi kết quả thành json_decode. Bước cuối cùng là thoát các dấu ngoặc đơn trước khi lưu trữ chuỗi vào MySQL.

Bằng cách này, tôi có thể lưu trữ bất kỳ chuỗi UTF8 nào trên máy chủ của mình.


0

Đây là một trong những làm việc cho tôi.

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
}

Tôi tìm thấy chức năng trên từ liên kết này: http://useyourloaf.com/blog/how-to-percent-encode-a-url-opes/

Bạn cũng có thể sử dụng chức năng này với phần mở rộng nhanh chóng. Xin vui lòng cho tôi biết nếu có bất kỳ vấn đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.