有时候想通过图片传递一些信息,比如公司电脑上一些自己的文档,拍照的话还得自己重新整理出来,要是能将文档转成二维码,用手机扫出来,多方便,本文对此进行一些尝试。
一
二维码能传输的信息量是很少的,直接将文档内容转成二维码很不现实,先对文档进行压缩再传输比较可行,先用WinRAR对原文件进行压缩。
然后需要将rar转换成可打印字符,一个汉字的二维码比两个ascii字符的二维码复杂,这里考虑将rar转成可打印的ascii字符。标准ascii里有95个可打印字符,那就选64个,可表示6个比特位。
rar文件 | 可打印字符 |
---|---|
6个比特位 | 一个字符 |
24个比特位/3个字节 | 4个字符 |
所以,rar文件的字节数和可打印字符的字节数之比是3:4。
二
将rar文件转换为可打印字符的代码:
// encode.c
//input: src.rar output: dest.txt
#include <stdio.h>
#include <string.h>
unsigned char table[]=
{
'0','1','2','3','4','5','6','7','8','9','<','>',
'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'
};
// 当到达文件尾不足3个字符时, 末尾补0, 最多会有2个空字符, 对rar文件无影响
int read3chars(FILE *fin, unsigned char *re)
{
int t;
unsigned char buf[3];
memset(buf, 0, 3*sizeof(unsigned char));
t = fgetc(fin);
if(t == EOF) return 1;
buf[0] = t;
t = fgetc(fin);
if(t == EOF) goto last;
buf[1] = t;
t = fgetc(fin);
if(t == EOF) goto last;
buf[2] = t;
last:
unsigned char a = (buf[0]&0xFC)>>2;
unsigned char b = (buf[0]&0x03)<<4 | (buf[1]&0xF0)>>4;
unsigned char c = (buf[1]&0x0F)<<2 | buf[2]>>6;
unsigned char d = buf[2]&0x3F;
re[0] = table[a];
re[1] = table[b];
re[2] = table[c];
re[3] = table[d];
return 0;
}
int main()
{
FILE *fin = fopen("src.rar", "rb");
if(fin == NULL)
{
puts("cann't open src.rar!");
getchar();
return -1;
}
FILE *fout = fopen("dest.txt", "w");
for(;;)
{
unsigned char buf[4];
int ret = read3chars(fin, buf);
if(ret == 1) break;
fputc(buf[0], fout);
fputc(buf[1], fout);
fputc(buf[2], fout);
fputc(buf[3], fout);
}
fclose(fin);
fclose(fout);
return 0;
}
三
将生成的字符分批次通过二维码扫描工具传输。
也可以直接改上面的main函数:
int main()
{
FILE *fin = fopen("src.rar", "rb");
if(fin == NULL)
{
puts("cann't open src.rar!");
getchar();
return -1;
}
char dest[] = "A.txt";
FILE *fout = fopen(dest, "w");
for(int i=1;;++i)
{
unsigned char buf[4];
int ret = read3chars(fin, buf);
if(ret == 1) break;
fputc(buf[0], fout);
fputc(buf[1], fout);
fputc(buf[2], fout);
fputc(buf[3], fout);
if(i == 256)
{
i = 0;
fclose(fout);
++dest[0];
fout = fopen(dest, "w");
}
}
fclose(fin);
fclose(fout);
return 0;
}
四
将扫码得到的字符组合在一起,放到txt文件里,然后执行以下的程序。这里并没有对BOM字符进行处理,仅仅是和上面的程序一起进行测试。
// decode.cpp
// input: dest.txt output: dest.rar
#include <cstdio>
#include <hash_map>
char table[]=
{
'0','1','2','3','4','5','6','7','8','9','<','>',
'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'
};
__gnu_cxx::hash_map<unsigned char, unsigned char> retable;
// 一定是4字节的整数倍
int read4chars(FILE *fin, unsigned char *ret)
{
unsigned char buf[4];
int t = fgetc(fin);
if(t == EOF) return -1;
buf[0] = t;
buf[1] = fgetc(fin);
buf[2] = fgetc(fin);
buf[3] = fgetc(fin);
unsigned char a = retable[buf[0]];
unsigned char b = retable[buf[1]];
unsigned char c = retable[buf[2]];
unsigned char d = retable[buf[3]];
ret[0] = a<<2 | b>>4;
ret[1] = b<<4 | c>>2;
ret[2] = c<<6 | d;
return 0;
}
int main()
{
for(unsigned char i=0; i<64; ++i)
retable.insert(std::make_pair(table[i], i));
FILE *fin = fopen("dest.txt", "r");
if(fin == NULL)
{
puts("cann't open dest.txt!");
getchar();
return -1;
}
FILE *fout = fopen("dest.rar", "wb");
for(;;)
{
unsigned char buf[3];
int res = read4chars(fin, buf);
if(res == -1) break;
fputc(buf[0], fout);
fputc(buf[1], fout);
fputc(buf[2], fout);
}
fclose(fin);
fclose(fout);
return 0;
}
五
这和Base64编码的原理是一样的。
网友评论