在Java/Clojure中将一个UTF-32编码的字符串(C风格)转换为UTF-16(JSON风格)编码的字符串。

3

我从一个服务中接收到了一个字符串,该服务显然使用UTF-32编码来编码其Unicode字符,如:\U0001B000(C样式的unicode编码)。但是,为了将此信息序列化为JSON,我必须将其编码为UTF-16,如:\uD82C\uDC00

然而,我不知道如何在Java / Clojure中读取这样编码的字符串,也不知道如何产生具有另一种编码格式的输出。

2个回答

2

您可以使用以下方式读取来自服务的接收字节:

(slurp received-bytes :encoding "UTF-32")

使用以下方式编写字符串:

(spit destination string-to-encode :encoding "UTF-16")

如果您的意思是您有一个表示编码字符二进制的字符串,那么您可以使用以下方法进行转换:

(defn utf32->str [utf32-str]
  (let [buf (java.nio.ByteBuffer/allocate 4)]
    (.putInt buf (Integer/parseInt (subs  utf32-str 2) 16))
    (String. (.array buf) "UTF-32")))

(utf32->str "\\U0001B000" )

然后使用以下方式将其转换为UTF-16:

(defn str->utf16 [s]
  (let [byte->str #(format "%02x" %)]
    (apply str
           (drop 1 (map #(str "\\U" (byte->str (first %) ) (byte->str (second %) ))
                        (partition 2 (.getBytes s "UTF-16")))))))

这是一个示例运行结果:
(str->utf16 (utf32->str "\\U0001B000"))
;=> "\\Ud82c\\Udc00"

它并没有按照我预期的方式工作。我尝试了(spit "resources/unicode.output" (slurp "resources/unicode.input" :encoding "UTF-32") :encode "UTF-16"),但它没有产生我在问题中提到的JSON / UTF-16编码。还请注意,我放入unicode.input文件中的字符串是\U0001B000,而我用slurp获取的是3个字节:0xFFFD 0xFFFD 0xFFFD - Neoasimov
你确定你的unicode.input是UTF-32编码吗?(spit "out.txt" (slurp "in.txt" :encoding "UTF-32") :encoding "UTF-16"),其中in.txt为0000000: 0000 feff 0001 b000 0000 000a,out.txt为0000000: feff d82c dc00 000a,这是运行xxd后的输出结果。 - Symfrog
@Neoasimov 注意,在调用 spit 函数时,你写成了 :encode 而不是 :encoding - juan.facorro
@Neoasimov,你能提供一份最小化的样本资源/unicode.input,并通过xxd运行它吗?例如:xxd resources/unicode.input - Symfrog
@Symfrog 太棒了!干净、简单且运行良好。正是我一直在寻找的。我之前还差很多才能达到这个水平 :) - Neoasimov
显示剩余4条评论

1

一旦你有了想要替换的字符串,以下函数将会实现它:

(defn escape-utf16
  [[_ _ a b c d]]
  (format "\\u%02X%02X\\u%02X%02X" a b c d))

(defn replace-utf32
  [^String s]
  (let [n (Integer/parseInt (subs s 2) 16)]
    (-> (->> (map #(bit-shift-right n %) [24 16 8 0])
             (map #(bit-and % 0xFF))
             (byte-array))
        (String. "UTF-32")
        (.getBytes "UTF-16")
        (escape-utf16))))

(replace-utf32 "\\U0001B000")
;; => "\\uD82C\\uDC00"

而且,为了进行有针对性的替换,请使用正则表达式:

(require '[clojure.string :as string])
(string/replace
   "this is a text \\U0001B000."
   #"\\U[0-9A-F]{8}"
   replace-utf32)
;; => "this is a text \\uD82C\\uDC00."

免责声明:我没有考虑过边缘(或者除了提供的之外的其他)情况。但是我相信您可以将这个作为进一步探索的基础。


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