Unicode 替换密码算法

2025-06-10

Unicode 替换密码算法

完全透明:我偶尔会在推特上浪费时间。(天哪!震惊!)我浪费时间在推特上的方法之一就是在个人资料里用不同的 Unicode 字符“字体”写我的名字,比如 𝖑𝖎𝖐𝖊 和 𝖙𝖍𝖎𝖘 𝖔𝖓𝖊。我以前的做法是在谷歌上搜索不同的 Unicode 字符,然后一个一个地复制粘贴到我推特个人资料的“姓名”栏里。因为这种浪费时间的方法有点浪费时间,所以我决定(以真正的程序员风格)编写一个工具,帮我在浪费时间的同时节省一些时间。

我把这个工具命名为uni-pretty。它允许你在输入框中输入任何字符,然后将它们转换成可以表示字母的 Unicode 字符,从而为你提供可以覆盖网站 CSS 的精美“字体”,就像在你的 Twitter 个人资料中那样。(抱歉,互联网。)

uni-pretty 截图

该工具的首次迭代只持续了大约二十分钟,当时我正在将 Unicode 字符复制粘贴到数据结构中。这种将字符存储在 JavaScript 文件中的方法被称为硬编码,它充满了问题。除了必须存储每种字体样式的每个字符之外,构建起来也很费劲,更新也很困难,而且代码越多,就越容易出错。

幸运的是,使用 unicode 意味着有一种方法可以避免存储所有字体字符的麻烦:unicode 数字是连续的。更重要的是,unicode 中可以用作字体的特殊字符(意味着字母表中的大多数或所有字母都有匹配的字符)始终遵循以下顺序:大写字母 AZ,小写字母 az。

例如,在上面这个奇特的 Unicode 代码中,小写字母“L”的 Unicode 编号U+1D591和 HTML 代码𝖑。序列中的下一个字母,小写字母“M”,有 Unicode 编号U+1D592和 HTML 代码𝖒。注意这些代码中的数字是如何递增 1 的。

这有什么意义呢?因为每个特殊字符都可以用数字表示,而且我们知道序列的顺序总是相同的(大写字母 AZ,小写字母 az),所以我们只需知道字体序列的第一个数字(大写字母“A”)就能生成任何字符。如果这让你想起了什么,可以借我的解码密码。

在密码学中,凯撒密码(或移位密码)是一种简单的加密方法,它利用一个字符替换另一个字符来编码信息。这通常使用字母表和一个移位“键”来完成,该键告诉你用哪个字母替换原来的字母。例如,如果我尝试将单词“cat”右移3位进行编码,结果将是这样的:

c a t
f d w
Enter fullscreen mode Exit fullscreen mode

有了这个概念,将纯文本字母编码为 Unicode“字体”就变得非常简单了。我们只需要一个数组来引用纯文本字母,以及 Unicode 大写字母“A”的首索引。由于某些 Unicode 数字也包含字母(虽然字母是连续的,但会带来不必要的麻烦),并且我们的目的是以 HTML 格式显示页面,因此我们将使用 HTML 代码编号𝕬,为了简洁起见,我们删除了多余的位。

var plain = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];

var fancyA = 120172;
Enter fullscreen mode Exit fullscreen mode

由于我们知道花式 Unicode 的字母序列与纯文本数组相同,因此可以通过使用纯文本数组中的索引作为花式大写字母“A”数字的偏移量来找到任何字母。例如,花式 Unicode 中的大写字母“B”等于大写字母“A”数字120172加上 B 的索引,即1120173

这是我们的转换函数:

function convert(string) {
    // Create a variable to store our converted letters
    let converted = [];
    // Break string into substrings (letters)
    let arr = string.split('');
    // Search plain array for indexes of letters
    arr.forEach(element => {
        let i = plain.indexOf(element);
        // If the letter isn't a letter (not found in the plain array)
        if (i == -1) {
            // Return as a whitespace
            converted.push(' ');
        } else {
            // Get relevant character from fancy number + index
            let unicode = fancyA + i;
            // Return as HTML code
            converted.push('&#' + unicode + ';');
        }

    });
    // Print the converted letters as a string
    console.log(converted.join(''));
}
Enter fullscreen mode Exit fullscreen mode

这种编码方法的一个巧妙的可能性在于,它需要偏离我最初的目的,即创建原始字符串的人类可读表示。如果目的是生成密码,fancyA那么只要索引的字符不是大写字母“A”的表示形式,就可以使用任何 Unicode 索引来代替 。

下面是使用简化的纯文本数组和非字母表示 Unicode 键设置的相同代码:

var plain = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];

var key = 9016;
Enter fullscreen mode Exit fullscreen mode

你可能会想到,一旦知道了编码密钥,解码用这种方法生成的密码会相对简单。你只需要从编码字符的 HTML 代码编号中减去密钥,然后在剩余的索引处找到相关的纯文本字母。

好了,今天就到这里。记得喝阿华田哦,我们下周一5:45见!

哦,还有…… ⍔⍠⍟⍘⍣⍒⍥⍦⍝⍒⍥⍚⍠⍟⍤ ⍒⍟⍕ ⍨⍖⍝⍔⍠⍞⍖ ⍥⍠ ⍥⍙⍖ ⍔⍣⍪⍡⍥⍚⍔ ⍦⍟⍚⍔⍠⍕⍖ ⍤⍖⍔⍣⍖⍥ ⍤⍠⍔⍚⍖⍥⍪

:)

鏂囩珷鏉ユ簮锛�https://dev.to/victoria/a-unicode-substitution-cipher-algorithm-59gm
PREV
Bash 和 shell 扩展:惰性列表制作
NEXT
使用 GitHub Actions 实现轻量级、与工具无关的 CI/CD 流程