命令行二维码

写在前面

发现一个有趣的东西,能在命令行显示二维码。日常一直是:

命令行起个sync服务
编辑器码码码,浏览器看看看
...码到差不多了
浏览器插件生成个二维码
掏出100个手机扫啊测啊
...咦,有问题
改改没好,场景比较复杂,得抽出来定位
复制个test文件
再生成个二维码,再扫再测,o了
...额,又发现个问题
...

浏览器二维码插件差不多能满足需求,在IDE、浏览器、终端、Finder之间频繁切换,虽然有点麻烦,但没有发现更好的办法……直到看见命令行二维码,怎么可以这么机智

一.原理

看到“命令行二维码”,想也知道原理是什么:输出到屏幕时可以控制前景色、背景色、文本样式

要输出二维码,只要能改背景色就够了,用白空格黑空格就能拼出来

例如:

# 输出focus here
# focus黑底白字,空格默认,here蓝底白字加粗
echo "\e[40;0;37mfocus\e[0m \e[44;1;37mhere\e[0m"

\e[0m重置为默认样式,一般控制序列格式为:

# \e[开头,m结尾
# 分号隔开的3个值分别是背景色、文本样式和前景色
\e[40;0;37m

支持的值如下:

# 文本样式
0: 常规文本
1: 加粗文本
4: 下划线文本

# 前景色30-37
30: 黑色
31: 红色
32: 绿色
33: 黄色
34: 蓝色
35: 紫色
36: 青色
37: 白色

# 背景色40-47
40: 黑色
41: 红色
42: 绿色
43: 黄色
44: 蓝色
45: 紫色
46: 青色
47: 白色

此外还有一些更强大的,比如移动光标、删除某行、清空屏幕等等,例如字符进度指示器:

echo -n '-'
arr=('\\' '|' '/' '-' '|' '100%')
for c in ${arr[@]};
do
    # 等1秒
    sleep 1
    # 左移一格,输出字符
    echo -en "\033[1D$c"
done

二.具体实现

1.生成二维码元数据

根据输入的字符串,按照二维码规则计算出二值矩阵

这个过程手动实现起来比较费劲,因为二维码是一个有几百项专利的技术,见http://www.qrcode.com/en/patent.html,需要买(弄)个ISO Specification实现一遍:

Obtaining QR Code Specification

QR Code is established as an ISO (ISO/IEC18004) standard. QR Code specification can, therefore, be purchased from this organization.

Purchasing ISO Standards

Please search by inputting ISO No.18004 or X0510 to “Search and ISO Catalogue”. http://www.iso.ch/iso/en/prods-services/ISOstore/store.html

不过不用那么费劲,因为有开源版本,且有各种语言实现:https://github.com/kazuhikoarase/qrcode-generator

但还是有必要了解一些概念:

  • 尺寸(spec里叫Version,上面的开源版实现里叫TypeNumber

    值为1401对应最小矩阵21x2140对应最大的177x177

  • 定位图案

    最明显的是二维码角角的3个回形框,3个点确定一个矩形。其它位置也有用来定位的图案

  • 纠错级别

    'L', 'M', 'Q', 'H'4个纠错级别,级别越高,容错率约高,携带的纠错数据也越多,所以才有个性化二维码

  • 数据类型

    保留数据(格式信息,版本信息,定位图案信息),实际数据信息和纠错数据

2.输出

上面提到的qrcode-generator主要js api如下:

// 创建实例
qrcode(typeNumber, errorCorrectionLevel) => QRCode
// 传入待编码串
addData(data, mode) => void

// 计算矩阵
make() => void

// 获取结果矩阵行列数(列数等于行数)
getModuleCount() => number
// 获取矩阵单元值
isDark(row, col) => boolean

元数据有了,遍历矩阵拼接黑白空格字符串,输出:

// 黑白空格
var black = "\033[40m  \033[0m",
    white = "\033[47m  \033[0m",
    toCell = function (isBlack) {
        return isBlack ? black : white;
    },
    repeat = function (color) {
        return {
            times: function (count) {
                return new Array(count).join(color);
            }
        };
    },
    fill = function(length, value) {
        var arr = new Array(length);
        for (var i = 0; i < length; i++) {
            arr[i] = value;
        }
        return arr;
    };

//...略去创建实例和计算矩阵部分

// 遍历
var border = repeat(white).times(qrcode.getModuleCount() + 3);
output += border + '\n';
qrcode.modules.forEach(function (row) {
    output += white;
    output += row.map(toCell).join(''); 
    output += white + '\n';
});
output += border;

// 输出
console.log(output);

然后就能在命令行显示一个大大的二维码了,如图:

terminal-qrcode

terminal-qrcode

3.命令行优化

但是,在命令行显示的话,尺寸太大

二维码最小尺寸也是21x21,黑框里21行几乎占满默认一屏了,码稍长一点就显示不全了,而一般url码都超过21行,例如:

// 在纠错级别为M(默认)时
// 21行的二维码只能显示14个字符
https://www.ay
// 22行的只能显示26个字符
https://www.ayqy.net/blog/
//...

所以要想办法让输出的二维码行列尺寸小一点,最容易想到的就是用奇怪的Unicode字符拼:

unicode-block

unicode-block

再把列空格数量减半,这样宽高尺寸就都能缩小一半,如图:

terminal-qrcode-small

terminal-qrcode-small

虽然丑一点,但仍然是可用的。那么还能不能更小,如果想缩小两半呢?

用同样的方法做不到,因为Unicode字符只有“下四分之一方块”、“下四分之三方块”和“左四分之三方块”、“左四分之一方块”,不够用了

三.开源npm包

很巧的是,上面提到的所有事情都有人做了:qrcode-terminal-alpha

把命令行二维码添进工作流工具里,构建完毕直接显示二维码,扫码会方便不少

参考资料

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code