URL 中的片假名字符 G 被错误编码

Katakana character ジ in URL being encoded incorrectly

我需要构建一个 URL,其中包含从我的应用程序服务器接收到的字符串路径,其中包含以下字符:ジ

然而,在 Swift 中,fileURLWithPath 似乎编码不正确。

let path = "ジ"
print(URL(fileURLWithPath: path))
print(URL(fileURLWithPath: path.precomposedStringWithCanonicalMapping))

同时打印:

%E3%82%B7%E3%82%99

这个预期的 URL 路径应该是:

%E3%82%B8

我错过了什么或做错了什么?感谢任何帮助。

您可以使用 dataRepresentation:

尝试这种方法
if let path = "ジ".data(using: .utf8),
   let url = URL(dataRepresentation: path, relativeTo: nil) {
    print("\n---> url: \(url) \n") //---> url: %E3%82%B8
}

有两个不同的字符,ジ。它们可能看起来相同,但它们具有不同的内部表示。

  • 前者是“片假名字母zi”,由单个Unicode标量组成,percent-encodes为%E3%82%B8

  • 后者仍然是单个Swift字符,但由两个Unicode标量(“片假名字母si”和“组合浊音标记”)组成,而这两个Unicode标量 percent-encode 到 %E3%82%B7%E3%82%99.

例如,可以使用 precomposedStringWithCanonicalMapping 规范化字符串中的字符。这可以将具有两个 Unicode 标量的字符转换为具有单个 Unicode 标量的字符。

但是您的本地文件系统(或者,至少 init(fileURLWithPath:))分解变音符号。本地文件系统确保变音符号以某种一致的方式编码是合乎逻辑的。 (参见 Diacritics in file names on macOS behave strangely。)为了本次讨论,它们被分解而不是预先组合的事实有点学术性。当您将它发送到服务器时,无论您的本地文件系统中发生了什么,您都希望它预先合成。

现在,您告诉我们“url 路径被服务器拒绝”。那没有意义。人们通常不会向远程服务器提供本地文件系统 URL。人们通常会从本地文件系统 URL 中提取文件名并将其发送到服务器。这可以通过多种方式完成:

  • 可以 在将文件名添加到服务器 URL 时使用 precomposedStringWithCanonicalMapping,并且它尊重该映射,与文件不同URL:

    let path = "ジ"      // actually `%E3%82%B7%E3%82%99` variant
    let url = URL(string: "https://example.com")!
        .appendingPathComponent(path.precomposedStringWithCanonicalMapping)
    print(url)          // https://example.com/%E3%82%B8
    
  • 如果在请求正文中发送,请使用 precomposedStringWithCanonicalMapping。例如。如果 multipart/form-data 请求中的 filename

    body.append("--\(boundary)\r\n")
    body.append("Content-Disposition: form-data; name=\"\(filePathKey)\"; filename=\"\(filename.precomposedStringWithCanonicalMapping)\"\r\n")
    body.append("Content-Type: \(mimeType)\r\n\r\n")
    body.append(data)
    body.append("\r\n")
    

现在,这些是关于如何向服务器提供文件名的两个随机示例。你的可能会有所不同。但想法是,当您提供文件名时,您会以规范格式预先组合字符串,而不是依赖于本地文件系统中的文件 URL 使用什么。

但我建议避免使用 URL(fileURLWithPath:) 来操作服务器提供的字符串。它仅在实际引用本地文件系统中的文件时使用。如果你只想 percent-encode 字符串,我建议使用 String 方法 addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)。这不会覆盖 precomposedStringWithCanonicalMapping 输出。