国际字符的 JavaScript 验证问题

Posted

技术标签:

【中文标题】国际字符的 JavaScript 验证问题【英文标题】:JavaScript validation issue with international characters 【发布时间】:2010-11-07 14:06:49 【问题描述】:

我们在 Stack Overflow 上使用出色的 validator plugin for jQuery 在将输入提交到服务器之前对其进行客户端验证。

它通常运作良好,但是,这让我们摸不着头脑。

以下验证器方法用于用户名字段的询问/回答表单(请注意,您必须退出才能在实时站点上看到此字段;它位于每个 /question页面和/ask 页面)

$.validator.addMethod("validUserName",
  function(value, element) 
  return this.optional(element) || 
  /^[\w\-\s\dÀÈÌÒÙàèìòùÁÉÍÓÚÝáéíóúýÂÊÎÔÛâêîôûÃÑÕãñõÄËÏÖÜäëïöüçÇßØøÅåÆæÞþÐð]+$/.test(value); ,
  "Can only contain A-Z, 0-9, spaces, and hyphens.");  

现在这个正则表达式看起来很奇怪,但其实很简单:

匹配字符串的开头 (^) 匹配其中任何一个.. 单词字符 (\w) 破折号 (-) 空格(\s) 数字 (\d) 疯狂的月亮语言字符(àèìòù等) 现在匹配字符串的结尾 ($)

是的,我们遇到了Internationalized Regular Expressions 问题。 javascript 对“单词字符”的定义根本不包括国际字符。

这是奇怪的部分:即使我们已经麻烦手动将大量有效的国际字符添加到正则表达式,它不起作用。你不能在用户名的输入框中输入这些国际字符而不得到..

只能包含 A-Z、0-9、空格和连字符

.. 验证返回!

显然验证适用于正则表达式的其他部分.. 那么.. 什么给出了?

另一个奇怪的部分是,此验证在浏览器的 JavaScript 控制台中有效,但在作为我们的标准 *.js 包含的一部分执行时无效。

/^[\w-\sÀÈÌÒÙàèìòùÁÉÍÓÚÝáéíóúýÂÊÎÔÛâêîôûÃÑÕãñõÄËÏÖÜäëïöüçÇßØøÅåÆæÞþÐð]+$/ .test('ÓBill de hÓra') === true

我们之前在 JavaScript 代码中遇到过一些非常奇怪的国际字符问题,导致了一些非常非常讨厌的黑客攻击。我们想了解这里发生了什么以及为什么。请赐教!

【问题讨论】:

这可能是字符编码问题吗?即,来自用户的疯狂月亮“Ä”不是您的正则表达式中的“Ä”? 我不知道答案,但这是写问题的好方法。 @Onorio Jeff 总是提倡提出写得很好的问题,所以他最好自己也这样做 :-) 但你肯定是对的。 é 不是来自月球语言的字符,神奇宝贝在英文字母表中不是吗?另请查看我的评论 Jorn 回答 【参考方案1】:

我认为电子邮件和 url 验证方法在这里是一个很好的参考,例如。邮件方式:

email: function(value, element) 
    return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`\|~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`\|~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
,

The script to compile that regex.

换句话说,用这个替换你的“疯狂月亮”字符的任意列表可能会有所帮助:

[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]

基本上,这通过用更通用的定义替换需要编码的字符来避免您在其他地方遇到的字符编码问题。虽然不一定更具可读性,但到目前为止它比您的完整列表要短。

【讨论】:

只是为了澄清为什么这样做。如果您的 .js 文件以字符编码形式进行编码,则其中的正则表达式中的所有字符都将以该编码表示,即使您的网页使用另一种编码也是如此。在我的项目中,我只是对可以包含 UTF-8 国际字符串的所有内容进行编码。这包括 .js 文件。 Jeff 可能发生的事情是,他的 .js 文件被编码为一个字符集,而他的页面被另一个字符集解析,他的 HTTP 请求/响应可能使用与页面相同的字符集进行编码。这解释了为什么它在调试器上工作。 另一件事,如果它显示正确,请尝试 alert("áéíóú") 您的 javascript 文件以与您的页面相同的编码进行编码。另一种解决方案是简单地包含您的 javascript: 其中 ISOsomething 是您的 .js 文件的编码。这是一个常见错误,因为大多数 IDE 以默认编码创建 .js 文件,默认情况下几乎从不使用 UTF-8。 答案中的两个链接都已损坏。 这对我有帮助,支持 i18n 字符和无双引号:^[a-zA-Z0-9!@#$%^~&*/?:'\,\\|()-_+\s\u00A0-\uD7FF\uF900-\uFDCF\uFDF0- \uFFEF]*$`【参考方案2】:

这不是一个真正的答案,但我还没有 50 个代表来添加评论...这绝对可以归因于编码问题。

是的,“ECMA 不应该关心编码......”等等等等,如果您使用的是 firefox,请转到 View > Character Encoding > Western (ISO-8859-1) 然后尝试使用名称字段。

手动更改编码后对我来说效果很好(假设页面的其余部分不喜欢编码开关,:P)

(在 IE8 上你可以去 Page > Encoding > Western European (Windows) 得到同样的效果)

【讨论】:

他是对的,这神奇地使名称:验证工作(!)【参考方案3】:

JS文件的字符编码是什么?

对于 XML QName,我使用这个 RegExp:

/**
 * Definition of an XML Name
 */
var NameStartChar = "A-Za-z:_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D"+
                    "\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF"+
                    "\uF900-\uFDCF\uFDF0-\uFFFD\u010000-\u0EFFFF";
var NameChar = NameStartChar+"\\-\\.0-9\u00B7\u0300-\u036F\u203F-\u2040";
var Name = "^["+NameStartChar+"]["+NameChar+"]*$";
RegExp (Name).test (value);

它也适用于国际化字符。注意转义。因此,我只能将 JS 文件限制为 ASCII 字符。因此,在处理 ISO-8859 与 UTF-8 字符集时我不会遇到麻烦。

如果您使用 ASCII 不是真正子集的字符编码(例如,在亚洲 UTF-16 中),这将不再正确。

干杯,

【讨论】:

据我了解,验证器规则位于外部 JS 文件中。然后我打赌该文件的编码错误(即不是 UTF-8)。 我在 Notepad2 中打开磁盘上的文件,它看起来正确 - 与您在 ANSI 中看到的相同,当我切换到 Unicode 时,UTF-8 编码也相同。 不可能。 ANSI 'Ä' (==ISO-8859-1) 具有单字节表示 'C4',而 UTF-8 'Ä' 在十六进制编辑器中看起来像 'C3 84'。 “开关”是什么意思?编码之间是真正的转换吗? 好吧,我在 Notepad2 中从服务器本身打开 .js 文件,并通过下拉菜单切换文件编码。对于正则表达式字符串,我看不出它们中有任何差异。我完全有可能做错了什么.. 奇怪的是,这在包含“ 【参考方案4】:

这里的比赛迟到了,但我只是使用了这个表达方式,它似乎对我很有效。似乎比较全面,也比较简单:

var re = /^[A-zÀ-Ÿ\s\d-]*$/g; 
var str1 = 'casa-me,pois 99 estou farto! Eis a lista:uma;duas;três';
var str2 = 'casa-me pois 99 estou farto Eis a lista uma duas três';
var str3 = 'àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ'

alert(re.test(str1));
alert(re.test(str2));
alert(re.test(str3));

【讨论】:

【参考方案5】:

列出的国际字符是扩展 ASCII 的一部分。你加的肯定不是。

【讨论】:

【参考方案6】:

鉴于该语句在控制台中有效,这是否必须按照您的 .js 文件的保存方式(即 ascii 或 UTF-8)以及浏览器正在加载它们并在此过程中翻译字符?

【讨论】:

JS 对 UTF-8 一无所知,即使编码是这样设置的。 但是浏览器可以,不是吗?如果文件加载为 UTF-8 并且浏览器的 JS 引擎由于浏览器错误加载文件而错误地解释了字符怎么办? 是的,浏览器在乎。如果将“Ä”另存为非 Unicode,则会导致无效的 UTF-8 字节流。因此,它永远无法匹配“Ä”对应的 UTF-8 字节流。 s/browser cares/browser 因此 JS 引擎 cares/【参考方案7】:

使用 Fiddler 或 Charles 之类的工具(不是 Firebug 的 Net 面板,或浏览器中实际存在的任何其他东西)来检查实际通过网络传输的内容。几乎可以肯定是编码问题:文件已保存在某些 Microsoft 字符集中并以 UTF-8 格式发送,或者可能是相反的情况。

在 JS RegExps 的情况下,正如 Boldewyn 指出的那样,您可以通过为您想要的 US-ASCII 范围之外的字符指定 Unicode 代码点来避免这些问题。不过,最好确保您没有混淆文件保存位置和提供文件位置之间的编码。

【讨论】:

gzip 在网络上,做起来很尴尬 Fiddler 和 Charles 都可以解决这个问题。 IIRC Fiddler(至少在版本 2 中)将在响应查看区域中为您提供一个按钮,以允许您查看解压缩的内容。

以上是关于国际字符的 JavaScript 验证问题的主要内容,如果未能解决你的问题,请参考以下文章

带有\b和国际字符的Javascript正则表达式问题

学习PHP中有趣的字符集国际化验证功能

java 和 JS(javaScript)中的反斜杠正则转义

在java中将国际字符串转换为\u代码

Java学习总计(二十六)——JavaScript正则表达式,Js表单验证,原生js+css页面时钟

如何检查 IBAN 验证?