我正在制作一个JavaScript应用程序,使用jQuery检索.json
文件并将数据注入到所嵌入的网页中。
.json
文件采用UTF-8编码,并包含带重音符号的字符,如é、ö和å。
问题在于我无法控制将使用该应用程序的页面的字符集。
有些页面将使用UTF-8,但其他页面将使用iso-8859-1字符集。这当然会破坏.json
文件中的特殊字符。
如何使用JavaScript将特殊的UTF-8字符转换为其iso-8859-1等效字符?
我正在制作一个JavaScript应用程序,使用jQuery检索.json
文件并将数据注入到所嵌入的网页中。
.json
文件采用UTF-8编码,并包含带重音符号的字符,如é、ö和å。
问题在于我无法控制将使用该应用程序的页面的字符集。
有些页面将使用UTF-8,但其他页面将使用iso-8859-1字符集。这当然会破坏.json
文件中的特殊字符。
如何使用JavaScript将特殊的UTF-8字符转换为其iso-8859-1等效字符?
实际上,所有内容通常都以某种Unicode形式内部存储,但是我们不去深究这个。我假设您之所以会看到"åäö"这种类型的字符串,是因为您使用的字符编码是ISO-8859。有一个技巧可以将这些字符转换。用于编码和解码查询字符串的escape
和unescape
函数是针对ISO字符定义的,而执行相同操作的新encodeURIComponent
和decodeURIComponent
则是针对UTF8字符定义的。
escape
将扩展的ISO-8859-1字符(UTF代码点U+0080-U+00ff)编码为%xx
(两位十六进制数),而将UTF代码点U+0100及以上的字符编码为%uxxxx
(%u
后跟四位十六进制数)。例如,escape("å") == "%E5"
且escape("あ") == "%u3042"
。
encodeURIComponent
将扩展字符作为UTF8字节序列进行百分比编码。例如,encodeURIComponent("å") == "%C3%A5"
且encodeURIComponent("あ") == "%E3%81%82"
。
所以您可以这样做:
fixedstring = decodeURIComponent(escape(utfstring));
escape("Ã¥") == "%C3%A5"
,这是将两个不正确的ISO字符编码为单个字节。然后,decodeURIComponent("%C3%A5") == "å"
,其中两个百分比编码的字节被解释为UTF8序列。utfstring = unescape(encodeURIComponent(originalstring));
有没有办法区分坏的UTF8字符串和ISO字符串?事实证明是有的。如上所述,使用decodeURIComponent函数时,如果给定了格式不正确的编码序列,它将抛出一个错误。我们可以利用这一点来很大概率地检测出我们的字符串是UTF8还是ISO。
var fixedstring;
try{
// If the string is UTF-8, this will work and not throw an error.
fixedstring=decodeURIComponent(escape(badstring));
}catch(e){
// If it isn't, an error will be thrown, and we can assume that we have an ISO string.
fixedstring=badstring;
}
问题在于一旦页面被提供,内容将会使用content-type元标签描述的编码呈现。 "错误"编码中的内容已经损坏。
在提供页面之前最好在服务器上进行这种处理。或者像我曾经说过的那样: 使用UTF-8端到端或死亡。
由于这个问题已经关闭,因此我在这里发布我的解决方案。该问题是如何从ISO-8859-1转换为UTF-8。
问题在于,如果您使用XMLHttpRequest获取任何内容,并且XMLHttpRequest.responseType为"text"或为空,则XMLHttpRequest.response会被转换为DOMString,这就是问题所在。然后,几乎无法可靠地处理该字符串。
现在,如果服务器返回的内容是ISO-8859-1格式,则必须强制响应类型为“Blob”,然后将其转换为DOMString。例如:
var ajax = new XMLHttpRequest();
ajax.open('GET', url, true);
ajax.responseType = 'blob';
ajax.onreadystatechange = function(){
...
if(ajax.responseType === 'blob'){
// Convert the blob to a string
var reader = new window.FileReader();
reader.addEventListener('loadend', function() {
// For ISO-8859-1 there's no further conversion required
Promise.resolve(reader.result);
});
reader.readAsBinaryString(ajax.response);
}
}
看起来神奇的事情发生在readAsBinaryString上,也许有人可以解释一下为什么这个方法有效。
在内部,Javascript字符串都是Unicode(实际上是UCS-2,UTF-16的子集)。
如果您通过AJAX单独检索JSON文件,则只需要确保JSON文件以正确的Content-Type和charset提供:Content-Type:application/json; charset="utf-8"
)。 如果这样做,当您访问反序列化对象时,jQuery应该已经正确地解释了它们。
您能否发布一下检索JSON对象所使用的代码示例?
function stringToBytes(text) {
const length = text.length;
const result = new Uint8Array(length);
for (let i = 0; i < length; i++) {
const code = text.charCodeAt(i);
const byte = code > 255 ? 32 : code;
result[i] = byte;
}
return result;
}
const originalString = 'ååå';
const bytes = stringToBytes(originalString);
const blob = new Blob([bytes.buffer], { type: 'text/plain; charset=ISO-8859-1' });
现在,请记住,一些应用程序确实接受UTF-8编码,但是除非您在前面添加BOM字符(如这里所解释的那样),否则它们无法猜测编码。
由于 escape
已被弃用(而且实际上对我没有起作用),因此我使用了一个小型库进行编码。 我选择了一个名为iso-8859-15的库。 请注意,ISO-8859-15与ISO-8859-1仅在少数字符上有所不同(comparison),您输入的内容很可能实际上是ISO-8859-15而不是ISO-8859-1。
import {encode} from 'iso-8859-15';
const encodedBytes = new Uint8Array(encode(unicodeString))
const blob = new Blob([encodedBytes])
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
escape
函数将扩展的 ISO-8859-1 字符(UTF 代码点 U+0080-U+00ff)编码为%xx
(两位十六进制数),而将 UTF 代码点 U+0100 及以上的字符编码为%uxxxx
(%u
后跟四位十六进制数)。例如,escape("å") == "%E5"
和escape("あ") == "%u3042"
。encodeURIComponent
函数将扩展字符作为 UTF8 字节序列进行百分号编码。例如,encodeURIComponent("å") == "%C3%A5"
和encodeURIComponent("あ") == "%E3%81%82"
。希望这能解决任何疑问。 - nitro2k01Uncaught URIError: URI malformed
,这是你提出的建议引起的。 - Luis A. Florit