Swift - 对包含特殊字符的字符串进行编码和解码

6
这里是我的问题:我正在开发一个包含即时聊天(使用FCM)的应用程序。一切都正常,只有在尝试发送特殊字符如“é”或表情符号时才出现问题。 收到的推送通知包含正确的字符串,但当讨论保存在服务器上并重新加载时,我无法阅读它们,并获得UTF8文本,例如“u00ea”,而不是“ê”。 我对编码等方面不是很了解,但如果您能帮忙,我将非常高兴!
这是我用来发送消息的代码:
    func sendMessage(_ completion: @escaping (_ response: String) -> ()) {

    DispatchQueue.global(qos: .userInitiated).async { () -> Void in
        let urlString = //Server adress
        let url = URL(string: urlString)

        var request = URLRequest(url: url!)
        let set = CharacterSet()
        request.httpMethod = "POST"
        let postString = "Message=" + /*String text to send*/
        request.httpBody = postString.data(using: String.Encoding.utf8)

    let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in

        })
        task.resume()
    }
}

非常感谢!

PS:我是第一次在stackoverflow上提问,请随时要求更多细节。

根据Rob的要求进行编辑: 我发送了消息“test é è”。 另一部手机通过推送通知接收到了“test é è”。 当一部手机从存储它的服务器加载讨论时,来自服务器的答案是:

    [{"content":"test u00e9 u00e8","sender":"103","timestamp":"1475491191"}]

此外,我不负责编写服务器代码,但正在负责的人同时制作了一款可以良好处理特殊字符的安卓应用。

编辑 #2:我也尝试了这段代码

    var request = URLRequest(url: url!)
        let set = CharacterSet()
        request.httpMethod = "POST"
        let postString = "Message=" + self.message.addingPercentEncoding(withAllowedCharacters: set)!
        let bytesArray = UTF8Encoding.decode(postString)

        request.httpBody = Data(bytes: UnsafePointer<UInt8>(bytesArray), count: bytesArray.count)

这没有任何影响。

非常感谢您的帮助。


U+00EA 是表示 "ê" 的 Unicode 代码点。但是你提供的代码看起来没问题,问题很可能出现在其他地方,比如数据在服务器上的解码、存储、加载或发送方式。你需要逐个调试每个环节!也许返回的是 JSON 数据,其中 "ê" 被编码为 \u00ea - Martin R
不,上述内容不正确。您必须对 httpBody 进行百分号转义。(此外,将 dataTask 分派到后台队列中没有意义...它已经是异步的了。) - Rob
1
没错,但我从"Message="这个参数推断出服务器期望的是application/x-www-form-urlencoded请求,如果是这样的话,他就需要对其进行百分号编码转义。或者你认为他没有使用x-www-form-urlencoded吗? - Rob
这是一个有效的参数。但是 "u00ea" 让我想到可能涉及到一些 JSON。 - Martin R
@Rob 我打印的是 NSString(data: data!, encoding: String.Encoding.utf8.rawValue) as? String,其中 data 是从URL会话接收到的数据,但是是的,就像我说的那样。 - Alexis Semmama
显示剩余8条评论
4个回答

8
您想使用addingPercentEncoding来对字符串进行百分号编码。但不要尝试使用像urlQueryAllowed这样的字符集,因为这会让一些关键字符(特别是&+)未经转义地传递。

作为一个很好的示例,可以看看Alamofire的ParameterEncodingescape例程。下面是一个作为扩展实现的版本:

extension String {
    public func addingPercentEncodingForQueryParameter() -> String? {
        return addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
    }
}

extension CharacterSet {
    static let urlQueryValueAllowed: CharacterSet = {
        let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
        let subDelimitersToEncode = "!$&'()*+,;="

        var allowed = CharacterSet.urlQueryAllowed
        allowed.remove(charactersIn: generalDelimitersToEncode + subDelimitersToEncode)

        return allowed
    }()
}

然后你可以这样做:
func send(message: String, _ completion: @escaping (_ response: String) -> ()) {
    let urlString = ...
    let url = URL(string: urlString)

    var request = URLRequest(url: url!)
    request.httpMethod = "POST"
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    let postString = "Message=\(message.addingPercentEncodingForQueryParameter()!)"
    request.httpBody = postString.data(using: .utf8)

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        ...
    }
    task.resume()
}

发送一个包含 & 的字符串,你应该会看到不同。 - Rob
如果您发送带有“&”符号的消息(例如“I went to Ben & Jerry's”),则要确保该符号完全传递,即正确地进行百分比转义。 - Rob
这个问题是正确的。有了它,你可以发送“&”符号。 - Nuno Vieira
非常感谢你,Rob。亿万次的感谢。<3 - iPeter

3

好的,我认为我明白了。

正如建议的那样,问题部分是由于服务器实现造成的。由于其安全性,它会过滤掉某些字符,这就是为什么它不能工作的原因。

我们已经纠正了问题,但当讨论从iOS到Android或从Android到iOS时,它仍然无法工作。

非常感谢您的帮助!

这是我的最终代码:

var request = URLRequest(url: url!)
request.httpMethod = "POST"
let postString = "Message=" + (self.message.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed))!
request.httpBody = postString.data(using: String.Encoding.utf8, allowLossyConversion: true)

0

我建议您使用Dictionary来存储非GET请求(例如POSTPUT等)的正文,并使用JSONSerializationencoding正文以UTF-8格式传递给URLRequesthttpBody

因此,不要这样做:

let postString = "Message=" + "Hello, World!"
request.httpBody = postString.data(using: String.Encoding.utf8)

使用这个:

let bodyDictionary: [String: Any?] = ["Message": "String text to send"]
request.httpBody = try? JSONSerialization.data(withJSONObject: bodyDictionary, options: [])

实际上它 @available(iOS 5.0, *) 并且需要最少的硬编码,这样可以确保您避免在代码中出现例如 "Message = " 的情况。


1
嗨,感谢回复!不过你可以看到这是一个非常老的帖子,现在已经没有什么用了。 无论如何,我今天使用字典 :) - Alexis Semmama
当然!太好了!实际上,我发现你的问题是因为我正在寻找类似问题的答案,所以我阅读了这篇文章,并决定为你和所有看到这篇文章的人得出我的结论。祝你的业务顺利! :) - eli7ah

-1

试试这个!jsonString?.addingPercentEncoding(withAllowedCharacters: .symbols) ?? ""


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