用户输入的一组明确清晰的字母和数字

38
是否存在一个易于阅读的字母数字子集?特别地,是否有一个子集拥有更少的视觉上具有歧义的字符,并通过去除(或等同)某些字符来减少人为错误?
我知道“视觉上具有歧义”是一个有点含糊的表达,但很明显,D、O和0都很相似,1和I也很相似。我想最大化字母数字集合的大小,但最小化可能被错误解释的字符数量。
我所知道的唯一关于这样一组的先例是加拿大邮政编码系统,它删除了字母D、F、I、O、Q和U,该子集是为帮助邮寄系统的OCR处理而创建的。
我的初步想法是仅使用大写字母和数字,如下所示:
A
B = 8
C = G
D = 0 = O = Q
E = F
H
I = J = L = T = 1 = 7
K = X
M
N
P
R
S = 5
U = V = Y
W
Z = 2
3
4
6
9
这个问题可能难以与给定的字体分离。所选择的字体中字符的独特性可能会极大地影响任意两个字符的潜在视觉歧义,但我预计,在大多数现代字体中,上述被等同的字符将具有足够相似的外观来证明它们是等同的。
对于上述内容,我会很感激您的想法 - 上述等式是否合适,或者还有更多应该等同的字符吗?小写字母是否更合适?

1
注意:在这里,“视觉上的歧义”是指人类,而不是OCR系统。所需解决方案是帮助手动输入。 - Ujjwal Singh
1
请参见http://ux.stackexchange.com/questions/21076/are-there-numbers-and-letters-to-avoid-for-activation-codes-via-sms。 - rwb
1
在 GitHub 上发布.. 预计 6 小时 - Ujjwal Singh
@UjjwalSingh,在Github上哪里? - Prof. Falken
@Prof.Falken 我还没有发布代码。你可能想看看这个链接:http://patents.stackexchange.com/q/13629/3127 - Ujjwal Singh
显示剩余4条评论
10个回答

22

我的一组23个明确无误的字符是:

c,d,e,f,h,j,k,m,n,p,r,t,v,w,x,y,2,3,4,5,6,8,9

我需要一组明确无误的字符用于用户输入,但我找不到任何其他人已经制作出符合我的标准的字符集和规则的地方。

我的要求:

  1. 不使用大写字母:这应该在URI中使用,并由可能没有太多打字经验的人输入,即使是Shift键也会减慢他们的速度并引起不确定性。我还希望有人能够说“全部小写”以减少不确定性,因此我想避免使用大写字母。

  2. 尽量少或不使用元音:避免创建污言秽语或令人惊讶的单词的简单方法是简单省略大多数元音。我认为保留“e”和“y”是可以的。

  3. 始终解决歧义:我愿意使用一些模糊的字符,只要我从每个组中只使用一个字符(例如,在小写字母s、大写字母S和数字5中,我可能只使用数字5);这样,在后端,我可以将这些模糊的字符中的任何一个替换为其组中的一个正确字符。因此,输入字符串“3Sh”将在我查找其数据库匹配项之前被替换为“35h”。

  4. 仅用于创建令牌:我不需要像base64或base32那样编码信息,因此除了我希望尽可能大之外,我的字符集中的确切字符数并不重要。它只需要用于生成随机UUID类型的ID令牌。

  5. 强烈偏好非模糊性:我认为让某人输入令牌并出现错误比让某人输入更长的令牌成本更高。当然有权衡,但我想强烈偏好非模糊性而不是简洁性。

我确定的混淆字符组:

  • A/4
  • b/6/G
  • 8/B
  • c/C
  • f/F
  • 9/g/q
  • i/I/1/l/7 - 太容易引起歧义了;请注意,欧洲“1”看起来很像许多人的“7”
  • k/K
  • o/O/0 - 太容易引起歧义了
  • p/P
  • s/S/5
  • v/V
  • w/W
  • x/X
  • y/Y
  • z/Z/2

明确无误的字符:

我认为这只留下了9个完全明确无误的小写字母/数字字符,没有元音:

d,e,h,j,m,n,r,t,3

从这些模糊的字符组中添加一个字符(并尽量选择看起来最不同的字符,避免大写字母),则有23个字符:

c,d,e,f,h,j,k,m,n,p,r,t,v,w,x,y,2,3,4,5,6,8,9

分析:

使用经验法则,UUID具有数值等价范围N,足以避免sqrt(N)实例的冲突:

  • 使用此字符集的8位UUID应足以避免约300,000个实例的冲突
  • 使用此字符集的16位UUID应足以避免约800亿个实例的冲突。

2
这是我最喜欢的一些明确字符列表。谢谢! - Samuel Neff

21

出于类似的原因(例如编码密钥等),我需要一个替代十六进制(基数16)的方法。我想到的最好的方法是以下16个字符,可以用作十六进制的替代:

0 1 2 3 4 5 6 7 8 9 A B C D E F     Hexadecimal
H M N 3 4 P 6 7 R 9 T W C X Y F     Replacement

在替换集中,我们考虑以下内容:
所有使用的字符都具有主要的区分特征,在真正糟糕的字体中才会省略。
为避免意外拼写单词,元音字母 A E I O U 被省略。
完全避免使用一些字体中可能非常相似或相同的字符集(任何一个集合中的字符都不被使用)。
0 O D Q 
1 I L J
8 B 
5 S
2 Z

通过完全避免使用这些字符,希望用户输入正确的字符,而不是试图纠正错误输入的字符。
对于一些相似度较低但可能混淆的字符集,我们只使用每个集合中最具有区分性的一个字符。
Y U V 

在这里使用Y,因为它总是具有较低的垂直部分,并且在衬线字体中具有衬线。
C G         

这里使用的是C,因为似乎C不太可能被输入成G,而反过来则更有可能。

X K         

这里使用X,因为它在大多数字体中更加一致。

F E         

这里使用F,因为它不是元音字母。

对于这些相似的字符集,输入集合中的任何字符都可以自动转换为实际使用的字符(每个集合中列出的第一个字符)。请注意,如果可能使用十六进制输入(参见下文),则不得自动将E转换为F。

请注意,替换集中仍然存在类似发音的字母,这几乎是不可避免的。在朗读时,应使用语音字母表。

在替换集中使用了标准十六进制中也存在的字符时,它们用于相同的十六进制值。理论上,可以支持十六进制和替换字符的混合输入,只要不自动将E转换为F即可。

由于这只是字符替换,因此很容易转换为/从十六进制。

输出的“规范”形式似乎最适合大写字母,尽管小写字母看起来也合理,除了“h”和“n”,在大多数字体中仍然应该相对清晰:

h m n 3 4 p 6 7 r 9 t w c x y f

输入当然可以不区分大小写。

有几种类似的基32系统,详见http://en.wikipedia.org/wiki/Base32。但是这些系统很明显需要引入更多相似的字符,以换取每个字符额外25%的信息。

据说以下字符集也用于Windows产品密钥的基24系统中,但同样具有更多相似的字符:

B C D F G H J K M P Q R T V W X Y 2 3 4 6 7 8 9

1
非常好的思考,感谢您提供这个答案。 - Brian M. Hunt
如果我没错的话,这里有一个微不足道的Python代码片段实现 - Brian M. Hunt

16
主要灵感来自此ux帖子,由@rwb提到,
  • Several programs使用类似的东西。您帖子中的列表似乎与这些程序中使用的非常相似,我认为对于大多数目的来说应该足够了。您可以始终将冗余(纠错)添加到“宽容”小错误;不过这需要您间隔代码(参见Hamming distance)。
  • 没有提到用于推导列表的特定方法的参考资料,除了人类的试错(这对于非OCR非常好:您的用户人类)。
  • 使用字符分组(例如,每5个一组)以增加上下文可能是有意义的(“第二组中的第一个字符”)。
  • 可以通过使用complete nouns(来自具有少量看起来相似物的字典;单词编辑距离在这里可能有用)而不是字符来消除歧义。人们可能会混淆“1”和“i”,但很少会混淆“one”和“ice”。
  • 另一个选择是将您的代码转换为可以大声朗读的(虚假)单词。markov model可以帮助你。

+1 用完整的名词;Cloudflare使用类似的名称服务器 - Jake Berger
错误校正在用户体验中可能被低估了。这里有一个有价值的部分可能是视觉距离度量-例如,O/D/0与C/Q更接近,但与I/H/R更远。尽管如此,在其他地方提到过,这可能严重依赖于字体。基于符号的错误校正(例如Reed Solomon)不依赖于视觉可能会更简单和更有效。这是一个非常棒的见解,tucuxi,谢谢! - Brian M. Hunt

5
如果您有使用仅大写字母的选项,我基于用户常常打错的字符创建了这个集合,但是这完全取决于他们读文本时使用的字体。
要使用的字符:A C D E F G H J K L M N P Q R T U V W X Y 3 4 6 7 9。
要避免的字符:
B similar to 8
I similar to 1
O similar to 0
S similar to 5
Z similar to 2

4
你所追求的是一种明确高效的人机代码。我的建议是使用具有实际意义的单词,尤其是名词来对整个数据进行编码。
我一直在开发一款能够做到这一点的软件,而且效率非常高。我称之为WCode。从技术上讲,它只是基于1024进制的编码方式,其中使用的是单词而不是符号。
以下是链接:
演示文稿:https://docs.google.com/presentation/d/1sYiXCWIYAWpKAahrGFZ2p5zJX8uMxPccu-oaGOajrGA/edit
文档:https://docs.google.com/folder/d/0B0pxLafSqCjKOWhYSFFGOHd1a2c/edit
项目:https://github.com/San13/WCode(请等待我上传...)

1
@BrianM.Hunt 请查看网站:http://WCodes.org。我还制作了一段视频,并在众筹网站IndieGoGo上发布了该项目:http://igg.me/at/wcode/x/2245741。 - Ujjwal Singh
现在可能更好选择使用BIP39,它可以将比特币的私钥编码成单词。https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki - undefined

3

对于人类来说,看起来不含糊的字母对于光学字符识别(OCR)也是不含糊的。通过删除所有对OCR具有困惑作用的字母对,可以得到:

 !+2345679:BCDEGHKLQSUZadehiopqstu

请查看https://www.monperrus.net/martin/store-data-paper


2
这将是OCR中的一个普遍问题。因此,为了实现端到端的解决方案,在OCR编码受控制时,专门开发了字体来解决您所提到的“视觉模糊”问题。 参见:http://en.wikipedia.org/wiki/OCR-A_font 作为附加信息:您可能想了解Base32编码-其中数字'1'的符号未使用,因为它可能会将其与字母'l'的符号混淆。

谢谢 - Base32是一个好的提示。严格来说,这个问题只涉及OCR,因为加拿大邮政先例中删除对机器阅读器不明确的字符。我对一组对人类来说不那么模糊的字符(或字形)集感兴趣。 - Brian M. Hunt
您可以在base32编码中使用自定义符号集,而实现部分保持不变。 - Ujjwal Singh

1
这是我编写的一些Python代码,用于使用上述字符系统对整数进行编码和解码。
def base20encode(i):
    """Convert integer into base20 string of unambiguous characters."""
    if not isinstance(i, int):
        raise TypeError('This function must be called on an integer.')     
    chars, s = '012345689ACEHKMNPRUW', ''
    while i > 0:
        i, remainder = divmod(i, 20)
        s = chars[remainder] + s
    return s


def base20decode(s):
    """Convert string to unambiguous chars and then return integer from resultant base20"""
    if not isinstance(s, str):
        raise TypeError('This function must be called on a string.')
    s = s.translate(bytes.maketrans(b'BGDOQFIJLT7KSVYZ', b'8C000E11111X5UU2'))
    chars, i, exponent = '012345689ACEHKMNPRUW', 0, 1
    for number in s[::-1]:
        i += chars.index(number) * exponent
        exponent *= 20
    return i


base20decode(base20encode(10))

1

这取决于您想要多大的集合。例如,只有{0,1}这个集合可能效果很好。同样,只有数字的集合也可以。但是您可能需要一个大约是原始字符集一半大小的集合。

我没有做过这个,但是这里有一个建议。选择一个字体,选择一个初始字符集,并编写一些代码来执行以下操作。将每个字符绘制到一个n乘n的黑白像素方格中,其中n从1到(比如)10。从边缘剪掉任何全白的行和列,因为我们只关心黑色区域。这给您每个字符的10个代码的列表。通过这些代码的差异数量来测量任意两个字符之间的距离。估计对于您的应用程序什么距离是可接受的。然后进行暴力搜索以找到相距那么远的字符集。

基本上,使用脚本模拟眯着眼睛看字符,看看哪些字符仍然可以分辨出来。


这在很大程度上取决于字体,甚至是字体大小。它可能还需要一些暴力对齐:LI共享很少的像素,直到您将垂直笔画放置重叠为止。 - tucuxi

-2

base58:123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz


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