SWIFT:NSURLSession将数据转换为字符串

5
在我的iPhone应用程序(使用SWIFT开发),我需要与一个https服务(带参数)进行通信,并且需要分析响应。所有工作都正常,但在某些情况下,我注意到它没有得到预期的结果...进一步分析后,我发现这是将服务器响应数据转换为字符串(NSData-> NSString)的问题...当我使用UTF8编码时,我得到nil作为转换后的字符串(responseString)。
    let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)

2). 但是使用ASCII编码就没有问题了(可以获得服务器提供的正确响应)

    let responseString = NSString(data: data, encoding: NSASCIIStringEncoding)

以下是我正在尝试的完整示例代码...

    let myUrl = NSURL(string: "https://myurl.com/myservice.asp")
    let request = NSMutableURLRequest(URL: myUrl!)

    request.HTTPMethod = "POST"
    request.timeoutInterval = 55.0
    let postString = "paramone=\(para1)&paramtwo=\(para2)&paramthree=\(para3)"

    // NOTE: Works ok with ASCII AND UTF8 both encoding types at this point...
    // request.HTTPBody = postString.dataUsingEncoding(NSASCIIStringEncoding)
    request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in

        if (error != nil)
        {
            println("Error: \(error)")
            println("Description: \(error.description)")
            println("Domain     : \(error.domain)")
            println("Error code : \(error.code)")
        }
        else
        {
            //???? => ENCODING PROBLEM
            // let responseString = NSString(data: data, encoding: NSASCIIStringEncoding)
            let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)

            println("Response: \(responseString)")
        }
    }
    task.resume()

我看到了其他几篇文章也在解释同样的问题...但不确定是否应该使用ASCII而不是UTF8...
此外,我无法理解回复中包含'£'符号,并且在ASCII编码下可以正常工作(即使'£'不在ASCII字符集中),但在UTF8下不能。
希望听到是否有遗漏或最佳实践...谢谢。
2个回答

7

NSASCIIStringEncoding被记录为严格的7位编码,用于ASCII值0到127。然而,实验表明当将NSData解码为(NS)String时,它接受任意数据,并将字节0到255解释为Unicode字符U+0000到U+00FF。因此,在解码时,NSASCIIStringEncoding的行为与NSISOLatin1StringEncoding完全相同:

let bytes = (0 ..< 256).map { UInt8($0) }
let data = NSData(bytes: bytes, length: bytes.count)

let s1 = String(data: data, encoding: NSASCIIStringEncoding)!
let s2 = String(data: data, encoding: NSISOLatin1StringEncoding)!
print(s1 == s2) // true

这可以解释为什么即使“£”不在ASCII字符集中,也能被正确解码。但请注意,这种行为(据我所知)没有被记录,因此您不应该依赖它。此外,这在将(NS)String编码为NSData时不起作用。
let d1 = s1.dataUsingEncoding(NSASCIIStringEncoding) // nil

如果服务器发送的 HTTP 响应头中带有 Content-Type = charset=... 字段,那么您可以自动检测编码,参见https://dev59.com/0I7da4cB1Zd3GeqP9ioG#32051684
如果服务器没有在 HTTP 响应头中发送响应编码,则只能尝试不同的编码。常用的编码有:
  • NSUTF8StringEncoding 用于 UTF-8 编码,
  • NSWindowsCP1252StringEncoding 用于 Windows-1252 编码,
  • NSISOLatin1StringEncoding 用于 ISO-8859-1 编码。
还有一个 NSString 方法可以检测使用的编码,但是这需要您先将数据写入文件,请参见Convert TXT File of Unknown Encoding to String

谢谢Martin R...我刚刚尝试了你在链接中建议的方法(let encodingName = response.textEncodingName),但是当我检查时,它是nil。不太确定为什么会是nil...因此我无法像你建议的那样获取'usedEncoding'...我可以使用'NSASCIIStringEncoding'来完成这件事,但正如你所说,这种行为没有被记录下来,感觉不安全... - JibW
@JibW:就像我之前说的,如果服务器没有发送使用的编码方式,那么你只能猜测。你可以尝试使用列出的三种编码方式(按顺序),并使用第一种转换不会返回 nil 的编码方式。 - Martin R
嗨,马丁R,谢谢。 最后一件事... 我不明白的是,“NSUTF8StringEncoding”在所有情况下都很好,除了一个情况。那时我能看到的唯一附加字符是“£”。 ASCII在这种情况下有效,而不是UTF8... 无论如何,该服务肯定应该在所有情况下使用相同的编码... 作为解决方案,我能做的就是像您建议的那样检查3种主要类型。 - JibW
@JibW:UTF-8和ASCII编码对于7位ASCII字符是相同的。其他像“à”这样的字符会产生相同的问题。 - Martin R
刚刚完成了一个小测试...让 responseString = NSString(data: data, encoding: NSStringEncoding()) 也可以工作...你知道这是什么吗...我认为这对其他人也会有帮助... - JibW
1
@JibW:NSStringEncoding() 只返回零值,这不是一个有效的编码。它被视为 ASCII 编码,但可能会导致警告,如“检测到不正确的 NSStringEncoding 值 0x0000。假定为 NSASCIIStringEncoding。将在不久的将来停止此兼容性映射行为。” - Martin R

6

@JibW 这段代码将帮助您分析响应并易于理解...根据您的要求进行一些额外的更改。!

@JibW 这段代码将帮助您分析响应并易于理解,根据您的需要进行一些额外的更改。请注意,不要修改HTML标签。

    let URL = NSURL(string: "Paste your url here")!
    var request = NSMutableURLRequest(URL: URL)
    var session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"
    request.timeInterval = 55.0
    var error: NSError?
    request.HTTPBody = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: &error)
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")


    var data: NSData!
    var response: NSURLResponse!

    var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in

        var httpResponse = response as! NSHTTPURLResponse
        println("\(httpResponse.statusCode)")

        var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
        println("Body: \(strData)")

         var err: NSError?
        var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as? NSDictionary

         if(err != nil) {
            println(err!.localizedDescription)
            let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
            println("Error could not parse JSON: '\(jsonStr)'")
        }

        else

        {
            // The JSONObjectWithData constructor didn't return an error. But, we should still
            // check and make sure that json has a value using optional binding.
            if let parseJSON = json {
                // Okay, the parsedJSON is here, let's get the value for 'success' out of it
                var success = parseJSON["success"] as? Int
                println("Succes: \(success)")
            }

            else {

                // Woa, okay the json object was nil, something went worng. Maybe the server isn't running?
                let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
                println("Error could not parse JSON: \(jsonStr)")
            }
        }
    })

    task.resume()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接