我可以依靠 charCodeAt() 和 fromCharCode() 的行为保持不变吗?
Posted
技术标签:
【中文标题】我可以依靠 charCodeAt() 和 fromCharCode() 的行为保持不变吗?【英文标题】:Can I depend on the behavior of charCodeAt() and fromCharCode() to remain the same? 【发布时间】:2011-09-08 16:30:44 【问题描述】:我编写了一个个人网络应用程序,它使用charCodeAt()
将用户输入的文本转换为相关的字符代码(例如⊇
转换为8839
进行存储),然后发送到Perl,它将它们发送到 mysql。为了检索输入文本,该应用使用fromCharCode()
将数字转换回文本。
我选择这样做是因为 Perl 的 unicode 支持很难正确处理。所以 Perl 和 MySQL 只看到数字,这让生活变得简单了很多。
我的问题是我可以依靠fromCharCode()
始终将像 8834 这样的数字转换为相关字符吗?我不知道它使用什么标准,但是假设它使用UTF-8,如果将来改为使用UTF-16,如果没有向后兼容性,这显然会破坏我的程序。
我知道我对这些概念的想法不是很清楚,因此如果我表现出误解,请注意澄清。
【问题讨论】:
您也可以使用escape
/unescape
或encodeURIComponent
/decodeURIComponent
对这些数据进行编码和解码。
【参考方案1】:
2018 年可以使用 String.codePointAt() 和 String.fromCodePoint()。
即使字符不在基本多语言平面 (BMP) 中,这些方法也有效。
【讨论】:
【参考方案2】:正如其他答案中所指出的,fromCharCode()
和 toCharCode()
处理基本多语言平面 (BMP) 中任何代码点的 Unicode 代码点。 javascript 中的字符串是 UCS-2 编码的,BMP 之外的任何代码点都表示为两个 JavaScript 字符。这些都不会改变。
要在 JavaScript 端处理任何 Unicode 字符,可以使用以下函数,该函数将返回一个数字数组,表示指定字符串的 Unicode 代码点序列:
var getStringCodePoints = (function()
function surrogatePairToCodePoint(charCode1, charCode2)
return ((charCode1 & 0x3FF) << 10) + (charCode2 & 0x3FF) + 0x10000;
// Read string in character by character and create an array of code points
return function(str)
var codePoints = [], i = 0, charCode;
while (i < str.length)
charCode = str.charCodeAt(i);
if ((charCode & 0xF800) == 0xD800)
codePoints.push(surrogatePairToCodePoint(charCode, str.charCodeAt(++i)));
else
codePoints.push(charCode);
++i;
return codePoints;
)();
var str = "?";
var codePoints = getStringCodePoints(s);
console.log(str.length); // 2
console.log(codePoints.length); // 1
console.log(codePoints[0].toString(16)); // 1d306
【讨论】:
感谢您的回答。到目前为止,我还没有移位位的经验(在这种情况下或任何其他情况下),并且很想对它们有一个很好的理解,你能推荐任何处理这些技术的书籍/资源吗?【参考方案3】:我选择这样做是因为 Perl 的 unicode 支持很难正确处理。
这是ɴᴏᴛ真的!
Perl 拥有所有主要编程语言中最强大的 Unicode 支持。如果您使用 Perl,使用 Unicode 比使用 C、C++、Java、C♯ 中的任何一种都容易, Python、Ruby、php 或 Javascript。这不是没有受过教育、盲目效忠的夸张和助长主义。这是基于十多年专业经验和学习的深思熟虑的评估。
天真的用户遇到的问题几乎总是因为他们在 Unicode 是什么上自欺欺人。头号最糟糕的大脑错误是认为 Unicode 类似于 ASCII 但更大。这是绝对和完全错误的。正如我在其他地方所写:
Uɴɪᴄᴏᴅᴇ 只是相对于 ᴀsᴄɪɪ 的一些扩大的字符集,这从根本上和批判性地说 不是真的。充其量,这仅适用于呆板的 ɪsᴏ‑10646。 Uɴɪᴄᴏᴅᴇ 包括更多,仅将数字分配给字形:整理和比较规则,三种形式的大小写,非字母大小写,多代码点大小写折叠,规范和兼容的组合和分解规范化形式、序列化形式、字素簇、分词和换行、脚本、数字等值、宽度、双向性、镜像、打印宽度、逻辑排序排除、字形变体、上下文行为、语言环境、正则表达式、组合类的多种形式、多个分解类型,成百上千非常有用的属性,还有更多‼
是的,很多,但这与 Perl 无关。它与Unicode有关。当您使用 Unicode 时,Perl 允许您访问这些东西不是一个错误,而是一个特性。那些其他语言不允许您完全访问 Unicode 绝不可以解释为对他们有利:相反,这些都是可能严重程度最高的主要错误,因为如果您无法工作如果 Unicode 在 21 世纪出现,那么这种语言是原始的、破碎的,根本无法满足现代文本处理的苛刻要求。
Perl 不是。用 Perl 做这些事情要比用其他语言容易得多。在他们中的大多数人中,您甚至无法开始解决他们的设计缺陷。你简直是完蛋了。如果一种语言不提供完整的 Unicode 支持,则它不适合本世纪;丢弃它。
Perl 使 Unicode 比那些不能正确使用 Unicode 的语言更容易做到。
在this answer 中,您会在前面找到在 Perl 中处理 Unicode 的七个简单步骤,在同一答案的底部,您会找到一些有用的样板代码。了解它,然后使用它。不要接受破碎。您必须先学习 Unicode,然后才能使用 Unicode。
这就是为什么没有简单的答案。 Perl 使使用 Unicode 变得很容易,假设您了解 Unicode 的真正含义。而且,如果您要处理外部源,则必须安排该源使用某种编码。
还阅读了我所说的关于 ?????? ?????????? 的所有内容。这些是你真正需要了解的事情。 Rule #49 的另一个损坏问题是 Javascript 损坏,因为它不会以完全相同的方式处理所有有效的 Unicode 代码点,而不管它们的平面如何。 Javascript 在几乎所有其他方面也被破坏了。它不适合 Unicode 工作。只需 规则 #34 就会杀死你,因为你无法让 Javascript 遵循关于 things like \w
在 Unicode regexes 中定义的所需标准。
令人惊讶的是,有多少种语言对 Unicode 完全没用。但是 Perl 绝对是不是其中之一!
【讨论】:
感谢您的回答。正是您引用的那个答案让我不想处理 Perl Unicode。对于我需要的东西(在数据库中保存偶尔的数学字符),将数据作为数字数组发送似乎更简单,而不是处理 Perl 和 MySQL 关于 Unicode 的复杂性。 顺便说一下,Perl 是我最喜欢的语言之一。可以看到我的头像是Higher Order Perl的封面图片。 :) 有点强硬,这个答案。我接受 JavaScript 对于 Unicode 工作来说并不理想,但对于客户端脚本并没有真正的替代方案,因此我们必须充分利用它。仍然 +1。【参考方案4】:在我看来,它不会崩溃。
阅读 Joel Spolsky 在Unicode and character encoding 上的文章。文章相关部分引述如下:
每个字母 字母表被分配了一个数字 Unicode 联盟是 像这样写:U+0639。这 数字称为代码点。 U+ 表示“Unicode”,数字是 十六进制。英文字母 A 会 是 U+0041。
这个神奇的数字是用 utf-8 还是 utf-16 或任何其他编码编码都没有关系。这个数字还是一样的。
【讨论】:
但是charCodeAt
没有给你一个代码点。参见例如developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
@Phillip:感谢您指出这一点。在这种情况下,这将是一个问题。【参考方案5】:
fromCharCode
和 toCharCode
处理 Unicode 代码点,即 0 到 65535(0xffff) 之间的数字,假设所有字符都在基本多语言平面 (BMP) 中。 Unicode 和代码点是永久性的,因此您可以相信它们永远保持不变。
编码 采用代码点(数字)流并输出字节流。 JavaScript 有点奇怪,根据 UTF-16 规则,必须通过两次调用 toCharCode
来构造 BMP 之外的字符。但是,您遇到的几乎所有字符(包括中文、日文等)都在 BMP 中,因此即使您不处理这些情况,您的程序也能正常工作。
您可以做的一件事是将数字转换回字节(大端 int16 格式),并将生成的文本解释为 UTF-16。 fromCharCode
和 toCharCode
的行为在当前的 JavaScript 实现中是固定的,永远不会改变。
【讨论】:
谢谢!如果用户输入的内容不在“基本多语言平面”中,会发生什么情况?toCharCode
是不是对付不了?
@Hawramani 不,它只返回两个看起来很奇怪的字符,它们不是 Unicode 代码点(第一个在 0xd800
和 0xdbff
之间,第二个在 0xdc00
和 0xdff
之间)。更新了答案。
所以fromCharCode
和toCharCode
显然不处理代码points,而是代码units。这意味着您必须处理单独的代码单元序列,即将它们转换为 JavaScript 或 Perl 端的标量值。
@Hawramani 是的,没错。您必须手动发出两个 UCS-2 代码点,而不是用一个 Unicode 字符编写 document.write(String.fromCharCode(0x1D49C))
,当它们在另一个宽度上组合时,将成为正确的东西。例如,document.write(String.fromCharCode(0xD835,0xDC9C))
。非常讨厌。【参考方案6】:
JavaScript 字符串是 UTF-16,这不会改变。
但不要忘记 UTF-16 是可变长度编码。
【讨论】:
可变长度编码是什么意思,它会影响我使用这两个函数来回转换字母的应用程序吗? @Hawramani:这意味着每个标量值由可变数量的 16 位代码单元表示。是的,这是您必须处理的事情,因为您不再使用代码点,而是使用代码单元。不过没那么难,只需在 Perl 脚本中明确说明您有一个 UTF-16 代码单元序列,而不是 Unicode 代码点序列。 @Philipp:嗯。我的程序所做的是将输入中的值转换为数组。例如,如果用户输入单词“Programming”,它就变成了这个数组:[80,114,111,103,114,97,109,109,105,110,103]
,然后将其作为字符串发送到 Perl,Perl 将其发送到 MySQL 而不进行任何处理。 MySQL 也仅将其视为字符串。所有的 unicode 处理都是在 JavaScript 中完成的,Perl 和 MySQL 可以使用 ASCII,它仍然可以工作。在我看来,您提到的内容在这种情况下不会引起问题,对吗?
@Hawramani:如何将代码单元数组转换为 Perl 字符串?以上是关于我可以依靠 charCodeAt() 和 fromCharCode() 的行为保持不变吗?的主要内容,如果未能解决你的问题,请参考以下文章