是不是努力Coding 就可以买大House

开源了。

让 AI 整理了下之前写的一个小工具

顺着教我怎么用Github Actions 自动化发布

于是 npm 仓库上 又留下了我的一个脚印

https://www.npmjs.com/package/qr-decoder-js
Qzone
修了下bug,现在识别更快,对于针式打印的蓝色二维码识别率也上来了

新注册了一个 www.dataerr.com
还不知道做什么,暂时用来做一些数据接口的中转服务。

从乱码到中文:前端二维码解码的一次实操记录

背景

客户那边有一套用了很多年的 ERP 系统,导出的二维码用手机扫一扫,呈现出来的是一串看不懂的乱码。用 Java 或者 Python 去读图片底层的字节数据,再做一次编码转换,是可以还原成正常中文的。但我们的场景是纯前端——用户在浏览器里上传图片或者打开摄像头,就要直接得到正确的结果,不能依赖后端服务。

前端这边折腾了挺久,最后用 zxing-wasm 配合一套自定义的解码逻辑,总算把这个问题解决了。

解码引擎的选择

zxing-wasm 是 ZXing C++ 库的 WebAssembly 版本,编译后可以直接在浏览器里运行。调用时把图像数据传进去,通过配置参数来启用各种内置的预处理能力:

const results = await readBarcodes(imageData, {
  formats: ['QRCode'],
  tryRotate: true,          // 自动旋转 0/90/180/270 度
  tryInvert: true,          // 尝试反色(黑底白字)
  tryHarder: true,          // 启用更精细的搜索
  tryDownscale: true,       // 大图自动缩小以加速
  tryDenoise: true,         // 形态学降噪
  maxNumberOfSymbols: 4,
});

灰度化、二值化、旋转修正、反色判断、降噪这些步骤,全部在 C++ 层内部完成,前端不需要额外处理,代码更简洁,识别率也有保障。

乱码问题的根源

引擎虽然能识别出二维码的内容,但遇到不带 ECI 字符集声明的中文二维码时,会默认按 UTF-8 解码。GBK 编码的中文在这种情况下就会显示成 汉字 这样的乱码——这是字符集探测本身的局限,不是引擎的 Bug。

readBarcodes 返回的结果里,除了 text(引擎自动解码后的文本),还有一个 bytes 字段,存放的是原始字节数组。问题就转化成了:拿到原始字节后,如何判断它到底应该用什么编码来解读。

暴力探测 + 评分择优

思路很简单:把常见的几种中文编码都试一遍,然后挑一个最像“正常文本”的结果。

function decodeBytesSmart(bytes: Uint8Array) {
  const encodings = ['gbk', 'gb18030', 'gb2312', 'utf-8', 'big5'];
  const attempts = encodings.map(enc => {
    try {
      const text = new TextDecoder(enc).decode(bytes);
      return { encoding: enc, text, score: scoreText(text) };
    } catch { return null; }
  }).filter(Boolean);
  attempts.sort((a, b) => b.score - a.score);
  return attempts[0];
}

关键是这个评分函数——怎么判断一段文本是“正常的”还是“乱码”?

function scoreText(t: string): number {
  if (!t) return -1e9;
  let s = 0;
  // 中文字符越多,得分越高
  const cn = t.match(/[\u4e00-\u9fff]/g);
  if (cn) s += cn.length * 5;
  // 以 { 或 [ 开头,可能是 JSON,加分
  if (t.trim().startsWith('{') || t.trim().startsWith('[')) s += 20;
  // 包含典型的 UTF-8 误读乱码字符,减分
  if (/Ã.|Â./.test(t)) s -= 30;
  // 高字节西欧符号,减分
  const garbage = (t.match(/[€‚ƒ…†‡ˆ‰Š‹ŒŽ'""•–—˜™š›œžŸ]/g) || []).length;
  s -= garbage * 10;
  return s;
}

中文字符多、有 JSON 结构,说明这段文本很大概率是正常的;出现 ÃÂ 这类 Latin-1 误读字符,或者大量高字节符号,基本就是乱码。用这个规则给每个编码尝试结果打分,分数最高的那个就是最可能的正确文本。

最后一步,把引擎自带的解码结果和暴力探测的最高分结果放在一起比较,谁分高就用谁:

const smart = decodeBytesSmart(target.bytes);
let text = target.text;
if (!text || scoreText(smart.text) > scoreText(text)) {
  text = smart.text;
}

结果

这套方案上线之后,无论是标准的 UTF-8 二维码,还是老 ERP 导出的 GBK 编码二维码,都能在浏览器里直接还原成正确的中文内容。整个过程不依赖后端,用户上传图片或打开摄像头扫码,得到的就是可读的文本。

网站恢复上线了。

微信又收到了打赏通知。

22年开始做的这个工具网站,最开始就寝室里用。

再后来开了源,上了在线版,修bug、优化性能,小白一个,跌跌撞撞弄了几年。

用户过了万,挺意外的。

今年五月,接口被攻击,上游把服务拉黑了。

只能暂时停掉,仓库也藏起来了,怕被人顺着逻辑继续搞。

原来攒的star也全没了。

那段时间在群里教大家怎么本地部署,能用的接着用。

最近工作没那么忙,把代码重构了,重新开源。

之前用的朋友也陆续回来了。

重新上线之后,开始有人打赏了。

感谢每一位。

Qzone
今天又被打赏了!开森!
Qzone
几百个star隐藏仓库就没了,在这里贴一下,重新赚star https://github.com/yanyaoli/byerun-web

深圳连着下了好几天雨,今天一天都没停过。

下班出来,雨还在飘着,不大不小,烦得很。

换了地方租房,也不能骑小电驴,扫了辆共享单车去地铁站。

自行车道跟人行道混在一起,电动车在旁边窜来窜去,得让着点。

路面坑坑洼洼的,雨天路滑,一手撑伞一手扶把,不安全。

就这么骑了一路,总算到地铁站。

收伞,甩了甩水,进站,刷卡,等车。

一天又过完了。

周末休息,跟女朋友去中医医院看脸上的痘痘。

反反复复长了好一阵,心情也不太好。

深圳还在下雨,打不到车,在路边拦了辆摩托车,小雨,让师傅慢点开。

预约时间快到了,但安全要紧。

医院有好几栋楼,约的是一位甘肃名医,独立的大办公室,挺气派。

把了脉,问了诊,开了药方,还有祛痘面膜和痛经的药。

下楼去药房办了快递,中药要熬,直接寄回去方便。

又上七楼,医生做了火针,一颗颗挑破,又用紫光灯照了十来分钟。

灯光太刺眼,没敢一直看。

灯停了,问医生可以走了。女朋友脸上大大小小的红点,看着有点心疼。

希望这次能好,别再受这番困扰了。

出来还在下雨,还是打不到车。

沿着坡往下走了一段,到了大公路才叫到车。

等车的时候,雨伞遮不住雨点,我们俩蹲在门庭下躲雨。

司机到了,车里空调开得足,一上车打了个寒颤。

没一会儿开始晕车,红灯多,走走停停,胃里翻得难受。

女朋友也累,靠着眯了一会儿。

总算到了,下车吸了口新鲜空气,缓过来了。

周五了。

一天也没什么事,和同事各自对着电脑。

到了下班点,关掉屏幕,收拾东西。

出了园区,夕阳泛着红。

周五晚上,路上人多,车也多。

明天不用早起,可以睡到自然醒。

地铁到了,上去,找个角落站好。

周五了,挺好。

广东 深圳

快下班了,瞄了一眼日期。

突然想起来,今天是好朋友生日。

发了条消息过去,约了晚上碰头。

女朋友今天没上班,提前过去点了菜和蛋糕。

我也提前撤了,在地铁口接上他。

商场就在旁边,坐下来边吃边聊,聊了些有的没的。

吃完饭在商场里逛了一圈,新开了家零食店,人挺多。

女朋友已经逛过买过了,我们也就没再买。

可惜是周一,不能痛痛快快玩一宿。

聚少离多,但感情没变过。

生日快乐。

几个周末都在看房,这周总算搬了。

下班回去就开始收拾,之前零零散散拾掇过一些,但最后一天还是得抓紧,东西比想象中多,堆了一地,一直收拾到八点半。

约的小厢车,九点司机准时到了。

车快开走的时候,女朋友想起来说电动车还没拉,我本来打算自己下班骑过去,差点忘了。

司机人挺好,没多说什么,帮忙一起把电动车抬上车。

我给他们买了瓶水,自己也喝了一瓶。

公寓管家来验房,转了一圈,没什么问题,第二天顺利退了押金。

关上门那一刻,住了一年的地方,说不清什么感觉,有点累,也有点不舍。

车开到新住处,又把东西一件件搬上去,电动车也安安稳稳拉过来了。

女朋友在家还有几天才上班,我第二天还得早起。

屋里东西摊了一地,也没力气细整了,把床铺出来,被子一裹就躺下了。

明天再说吧。

新地方,从今晚开始住。

广东 深圳
liyaqi
其实还有几只猫咪趴在旁边的车上。表情

周四了,要放假了😭

广东 深圳

周四了,要放假了😭

广东 深圳

周四了,要放假了😭

广东 · 深圳

做了一个梦。

公司转型升级了。

我的工作是去高中当一个学生。

梦里坐在教室里,好几个同学也在,但看不清。

下课铃响的时候醒了一下,又睡过去,继续梦。

梦到课间趴在桌上补觉。

醒来的时候人在床上,周一了。

躺着想了想,上班要是改成去当学生,也不是不行。

看了一眼手机,没有通知说公司转型的事。

起床上班。

广东 深圳

一不小心两点半了,睡了睡了。

广东 深圳

建议不要在520发誓啊喂,大暴雨天让牛马怎么上班 😭

广东 深圳

@我是柚子:我爸叫鄢水平,用我妈的话讲,他这辈子混得挺没水平的。

他初中辍学跟人学了半年屠宰,之后二十多年在外做工地建筑工人了,也没混到过个包工头当当,最后一笔工钱还被跟了几年的包工头抹了五十多块钱的零头,五十岁刚过,还完家里最后一笔债,人就没了。

他排行第六,是七个姊妹兄弟里面唯二的男丁,听家里人讲,小时候有什么都是先紧着大的,他从小也因为家里穷没过上啥好日子。

后面有了我和姐姐,要自建房、要供我们读书,他就更省了:没有买过一件羽绒服,作为家里最怕冷的,每年冬天也只是一件又一件叠穿不防寒的毛衣,永远穿着我初高中的鞋子,一把铁锤用了十几年,柄要脱落了也没换过,为了省五十块钱选通宵的硬座火车票而不是硬卧…

其实我和他感情本来不太深的,从我出生到他死的二十来年里,他基本都只会过年会回家,小小的我自作聪明地以为他就是不爱我,所以我也不用多对他好。

我的童年也好像一直缺失着父亲的角色,我遇事也从来不会跟他打电话,不会和他说我的事,我们的交流固定在过年的几天在家一起吃饭,以及每年过年和生日的分别一个200块红包。

23年过年,因为我22年复读高考完,成了全家也是全村第一个考上985的,他一定拉着我和他去姑姑们家走亲戚,以前的我都因为和他们不亲,不愿意去他也不会强求,可这次他一定坚持要我陪他去,我想他是想弯了一辈子的腰板,可以挺直一次。

但面对的是姑父们的各种挖苦和瞧不起,他们说我们家一直都没起色没出息,会读书也没什么用,我很愤懑,为什么他都不会回嘴,为什么要受这种窝囊气,回家后我就对他发脾气说,我再也不会跟他一起去走亲戚去受气了!

24年初过年,他这一年没有喊我一起去走亲戚了,不想我跟着受气受委屈吧,他自己去的时候,我不瞥见他脑后的错落的白发,才意识到这个一米六几、不到一百斤的男人慢慢老了。我突然意识到我得多爱他一点的,我发了个私密朋友圈说等我大二结束要拿好多奖学金,明年过年的时候要给他买最暖最厚的羽绒服,要提前给他买好卧铺票。

那年三月,你出去打工不久就从二楼工地摔下来了,右脚被划破真菌感染,全部紫红的鼓肿起来了,当然这些都是在你死了之后,我才通过你手机相册知道的,毕竟我基本从来不会主动找你了解你的近况。

五月底你身体疼得受不了,和舅舅一起去医院检查,医生说你还有本事干工地啊?肝癌晚期,随时都可能不行了,叫你赶紧回家里和亲人告别吧。你打通妈妈的电话,第一句说的是对不起,出门几个月,没给家里带来什么好消息还得了恶病。

六月初是我生日,你不同以往的,一次性给我发了两个红包,现在想来,是提前把过年的压岁钱给我了,你估计自己挺不到那个时候了。因为我在期末考试,你和妈妈商量了,让所有亲戚都只对我隐瞒了,发完生日红包之后到你死的一个月里,你一句话也没和我说过了。

六月底你实在疼得受不了了,才跟家里人说要让姐姐知道,要让姐姐回家,你总是怕耽误我们,怕耽误姐姐工作,怕耽误我读书,可我们耽误你的一生,你怎么不怨我呢?

七月三号,我结束完期末考试,姐姐偷偷试探我的考试结束与否,说了爸爸的病情。刚刚爸爸跟她讲恐怕要守夜了,可能挺不过这两天了,不知道能不能等到我回来了,这也是他留给我的最后一句话。当晚我凌晨三点搜索着肝癌晚期的症状,心疼地哭着睡不着觉,在我第二天第一次登上飞机,收拾了很多拍立得相纸要和他拍,拍下了飞机上很好看的云和晚霞。

等我回去了,才知道你在姐姐跟我说完消息的四个小时之后就走了。你是在凌晨十二点多走的,长辈说,咱那边人如果在晚上十二点前走就可能会把后辈以后的福气,早午晚饭都带走,你硬生生不愿意咽气,挨到的凌晨十二点后,你还是怕耽误我和姐姐。

第二天回家的我能做的就只有在你走的那个房间外磕了三个头,再见你,是在火葬场从冷冻柜推出来的,穿着寿衣,被病痛折磨的只剩皮包骨,我不敢认你,跪着哭着给你磕头,我不信,半年不见的人就再也不能见了。

火化了捧着你的骨灰盒我们回了家,把你安葬在家不远的山头,风水先生说那一块很适合你,前有水后有路,在地下会住得很舒坦。你是不是住得太舒服都不舍得给我托梦,还是在怪我没有好好关心你呢?

面对购物车那件没来得下单的羽绒服,面对没来得及拆封和你拍的相纸,面对相册拍下的那么好看的天上的云和天,我该和谁说呢?

你那么怕冷,是因为担心我给你买羽绒服费钱,所以才永远留在最热的夏天吗

你儿子在你死了的一年里,一直都努力奔前程,一直努力赚学费。在你的一周年忌日当天,结束了北大的面试,后面还顺利通过了,但是学费有点贵,我一边实习赚钱一边哭,我付不起。

后来我也去了清华面试,但是发挥的不好,面试的前一个晚上哭到两点睡不着,结果是没过,面对着十几万的北大学费,我自责地哭得停不下来,是不是我哭得太大声你心疼地受不了,后面才会破天荒地被清华候补录取,垫底倒数着进去了,不用太担心学费问题了。今年暑假我借了朋友的北大录取封壳,把北大优营邮件打印出来烧给你你有没有收到?

又要过年了,你能回来看看我吗?

鄢水平,其实你当爸爸很有水平
#爸爸 #我爱你 #亲人的离世不是一场暴雨而是一生的潮湿 #争气 #清华大学

我爸叫鄢水平,用我妈的话讲,他这辈子混得挺没水平的...

降温了😴

广东 深圳