验证字符串仅包含ASCII字符和数字

3

我正在进行用户名验证:

if [[ "$username" =~ ^[a-z][_a-z0-9]{2,17}$ ]]; then

实际上,用户名可以包含像é、ç、à等Unicode字符。

我应该使用哪个正则表达式类来限制字符串仅为ASCII字母(a、b、c、d……z)?

4个回答

3

您可以通过首先设置LC_ALL=C(可能是临时的,以免影响其他内容)来实现这一点。更现代的正则表达式引擎允许使用区域设置,可以将带重音符号的字符折叠到它们的基本字符上(或者至少按顺序排列,使它们处于az之间)。

由于C语言环境只知道ASCII,因此这应该可以解决问题。

例如,请参见以下脚本:

#!/bin/bash

username=amélie_314159

for locale in '' 'C' ; do
    export LC_ALL="${locale}"
    printf "LC_ALL set to %-3s: '%s' is " "'$LC_ALL'" "${username}"
    if [[ "${username}" =~ ^[a-z][_a-z0-9]{2,17}$ ]] ; then
        echo valid
    else
        echo invalid
    fi
done

它将输出:

LC_ALL set to '' : 'amélie_314159' is valid
LC_ALL set to 'C': 'amélie_314159' is invalid

我仍然希望将字符串视为Unicode字符串,特别是在脚本的其余部分。而且我不确定将LC_ALL设置为“C”可能会产生什么其他影响。 - John Smith Optional
@JohnSmithOptional,这就是我建议你本地化 LC_ALL 设置效果的原因。换句话说,只在 if 语句中设置它,然后恢复默认设置。 - paxdiablo

3
弹性设计的方法是将[a-z] 直接拼写为 [abcdefghijklmnopqrstuvwxyz]。这样就不用考虑区域设置或有趣的字符类,而且自1970年1月1日00:00:00以来,任何Shell都支持它。无论您的操作系统供应商、Shell供应商、Unix标准化过程或BOFH认为什么是炫酷的,这个方法都是未来的。

加一个额外的变量lc,像这样:

lc=abcdefghijklmnopqrstuvwxyz

正则表达式甚至变得易读:

[$lc][_0-9$lc]{2,17}

这就是高度健壮且可移植的configure脚本所做的事情。

直到你停止假设地球上每个人都有一个英文名字,并且你想允许非拉丁字符,才能真正做到防弹。开个玩笑,它回答了问题,但无论如何,^[abcdefghijklmnopqrstuvwxyz][_abcdefghijklmnopqrstuvwxyz0-9]{2,17}$ 是一个非常丑陋的正则表达式 :-) - paxdiablo
很不好的是,[a-z] 的语义在环境上有一个隐藏的依赖性,这让 OP 感到惊讶。使用 lc=abcdefghijklmnopqrstuvwxyz,正则表达式变得非常易读,为 [$lc][_0-9$lc]{2,17} - Jens
嗯,只有当你对国际化和本地化没有太多经验时它才是“隐藏的”,但我有这个优势,因为我在一家必须将产品运往许多不同国家的公司工作。但是你的 $lc 技巧使你的解决方案更加可行,所以为此点赞。我建议将其添加到答案中而不是留在注释中。 - paxdiablo
我现在打算采用这个解决方案。它可能有点“丑陋”,但它是明确的,我相信多年后我仍然能理解脚本的工作原理。你说得对:[a-z]的语义很丑陋。我可能会将你的答案标记为已接受,但我会等待几个小时,以防有人找到我们迄今为止没有想到的解决方案。 - John Smith Optional
@JohnSmith 可选明确、自我记录、未来可靠:总之——优雅 - Jens

0
请使用以下内容:
if [[ "$username" =~ ^[\x00-\x7f]{2,17}$ ]]; then

我真的很想做到这一点(或更准确地说是 ^[\x61-\x7a]$,因为我只对 a、b、c...z 感兴趣)。不幸的是,似乎 bash 正则表达式不理解字符的 \x.. 表示法。如果你找到了让它工作的方法,那就太棒了。 - John Smith Optional

0
在使用正则表达式检查用户名长度是否正确之前,您应该对输入字符串进行清理。这意味着您应该将允许的内容列入白名单,而不是列入不允许的黑名单。在这种情况下,我们可以在实际的正则表达式之前使用例如tr来删除所有不需要的字符。当正则表达式检查跟随时,它变得更加简单。
echo "abc[日本語][ひらがな][カタカナ][äüéçà]abc" | tr -dc "a-zA-Z"

这将只留下abcabc作为余数。


1
这很可能存在原始正则表达式的所有问题,即依赖于区域设置来确定a-z的含义。而且你使用它是错误的,因为tr不接受像[a-z]这样的字符类,而是只将-视为特殊字符。你的管道没有过滤掉[] - Jens
谢谢你关于括号的提示,我搞错了(已经修正)。不幸的是,你说这不起作用是错误的,请看修改。 - Bjoern Rennhak
它只允许字母表中的内容进入白名单是错误的,因为它还允许[]进入白名单。 - Jens

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