一次「字符洁癖」引发的思考:我用 AI 写了个全角半角转换工具

最近在整理项目文档时,遇到一个让我血压飙升的问题------从微信、PDF 或网页复制过来的文本,总是夹杂着各种「不三不四」的字符。明明看起来是正常的英文和数字,但粘贴到代码里就报错,搜索也搜不到。

排查了半天,罪魁祸首居然是全角字符 。全角的 ABC 和半角的 ABC 看起来几乎一样,但在程序眼里完全是两个不同的东西。这种「视觉混淆」在开发中造成的坑,相信不少朋友都踩过。

痛点:全角字符的「隐形炸弹」

在中文技术社区混久了,你会发现全角/半角问题是个「经典老番」。无论是:

  • 从微信聊天记录复制代码片段
  • 从 PDF 文档提取文本
  • 从 Word 粘贴到终端或编辑器
  • 处理用户提交的表单数据

全角字符总能以各种姿势给你「惊喜」。最典型的场景是------用户从某个中文输入法切到英文模式时,标点符号还是全角的。于是 if (x > 5) 变成了 if(x>5),直接编译报错。

传统解决方案无非是:手动逐个替换(效率低到让人崩溃)、写个简单的正则替换(但边界情况多如牛毛)、或者用一些重量级工具(杀鸡用牛刀)。

为什么不用现成的库?

说实话,市面上确实有不少现成的转换库,比如 fullwidth-halfwidth、wcwidth 之类的。但在我的场景里,有几个考量:

  1. 体积问题:为了一个转换功能引入整个库,对轻量级工具来说有点「奢侈」
  2. 依赖管理:独立开发的小工具,能少一个依赖就少一个
  3. 可控性:自己实现,遇到特殊字符可以随时调整映射规则

于是,我决定用 AI 辅助开发一个纯前端、零依赖的转换工具。

和 AI 协作:从「能用」到「好用」

第一轮:给 AI 提需求

我用的方式是先给 AI 一个清晰的 prompt,描述需求和约束:

markdown 复制代码
写一个纯 JavaScript 的全角半角转换函数,要求:
1. 全角转半角
2. 半角转全角
3. 处理全角空格 U+3000
4. 处理全角标点 U+FF01-U+FF5E
5. 特殊货币符号映射
6. 不要修改原字符串
7. 处理 null/undefined 输入

AI 第一次生成的代码其实已经能跑,核心逻辑就是 charCodeAt 判断 + 偏移量加减。但有几个问题:

javascript 复制代码
// AI 第一版的问题代码
function toHalfWidth(str) {
  if (!str) return str;  // 问题:null 和 '' 都返回 null
  return str.replace(/[\uFF01-\uFF5E\u3000]/g, function(char) {
    return String.fromCharCode(char.charCodeAt(0) - 0xFEE0);
  });
}

这里有个典型的「AI 陷阱」:if (!str) 会把空字符串和 null 混淆,而且用正则替换的方式虽然简洁,但对特殊字符(如全角货币符号)的处理不够灵活。

第二轮:调优边界情况

我告诉 AI 这几个问题后,它迭代出了第二版,改用逐字符遍历的方式,并且把特殊映射表单独抽出来:

javascript 复制代码
var FWSpecial = {
  0xFFE0: '¢', 0xFFE1: '£', 0xFFE2: '¬',
  0xFFE3: '¯', 0xFFE4: '¦', 0xFFE5: '¥', 0xFFE6: '₩'
};

function toHalfWidth(str) {
  if (str == null) return '';
  var out = '';
  for (var i = 0; i < str.length; i++) {
    var c = str.charCodeAt(i);
    if (c === 0x3000) {
      out += ' ';
    } else if (c >= 0xFF01 && c <= 0xFF5E) {
      out += String.fromCharCode(c - 0xFEE0);
    } else if (FWSpecial[c]) {
      out += FWSpecial[c];
    } else {
      out += str.charAt(i);
    }
  }
  return out;
}

这一版明显靠谱多了,但 AI 的「过度设计」倾向也暴露了------它加了很多不必要的类型检查,比如 typeof str !== 'string' 之类的防御代码。对于这个场景,str == null 的判断已经足够了。

第三轮:处理「看不见」的坑

真正的坑在测试阶段才暴露出来。我拿了一段从微信复制的文本测试,发现全角空格 U+3000 被正确转换了,但全角逗号 , 和全角句号 。 却原样保留。

原因是:微信复制出来的文本中,全角标点其实是 Unicode 中的「CJK 标点符号」区块(U+3001-U+303F),而不是全角 ASCII 区块(U+FF01-U+FF5E)。这俩完全是两回事。

我查了一下资料,发现这是个「历史遗留问题」------Unicode 在设计时把「全角 ASCII 变体」和「CJK 专用标点」分开了。于是跟 AI 讨论后,决定保持现状:只处理 U+FF01-U+FF5E 和 U+3000,CJK 标点保留原样。

这也是个很重要的设计决策:不要过度转换 。如果把 , 转成 ,,反而可能破坏中文排版的美感。

技术实现细节

最终的核心逻辑其实很简单,就是一个映射表 + 遍历:

javascript 复制代码
function toHalfWidth(str) {
  if (str == null) return '';
  var out = '';
  for (var i = 0; i < str.length; i++) {
    var c = str.charCodeAt(i);
    if (c === 0x3000) {
      out += ' ';  // 全角空格
    } else if (c >= 0xFF01 && c <= 0xFF5E) {
      out += String.fromCharCode(c - 0xFEE0);  // 全角 ASCII 变体
    } else if (FWSpecial[c]) {
      out += FWSpecial[c];  // 特殊货币符号
    } else {
      out += str.charAt(i);  // 其他字符原样保留
    }
  }
  return out;
}

反向转换 toFullWidth 就是反过来,把 0x21-0x7E 加上 0xFEE0 偏移。

这里有个有意思的点:为什么偏移量是 0xFEE0?因为 0xFF01 - 0x21 = 0xFEE0。全角字符和半角字符在 Unicode 里就差这个偏移量。

和 AI 协作的心得

这次开发让我对 AI 辅助编程有了新的认识:

  1. AI 擅长「骨架」,不擅长「边界」:它能快速生成核心逻辑,但边界情况(null、特殊字符、浏览器兼容性)往往需要人工补充
  2. prompt 要具体:给 AI 的描述越清晰,生成的代码质量越高。比如明确写出「处理 U+3000 全角空格」比「处理全角字符」效果好得多
  3. 测试驱动对话:拿真实场景的文本去测试 AI 生成的代码,把失败案例反馈给它,迭代速度会很快
  4. AI 会「过度设计」:有时候它会加一些不必要的防御代码,或者过度封装。这时候需要自己判断哪些是必要的

适用场景

这个工具最适合的场景:

  • 数据处理:清洗从各种渠道复制来的文本
  • 代码修复:处理用户输入中的全角字符
  • 文本规范化:统一文档中的字符格式
  • SEO 优化:避免因全角字符导致的搜索匹配问题

一点小思考

全角/半角问题看似简单,但背后涉及 Unicode 编码、字符映射、中文排版等多个领域的知识。这也是为什么我决定自己实现而不是直接用库------在解决「简单」问题的过程中,能学到不少「不简单」的细节。

而且,纯前端实现还有个好处:数据完全在本地处理,不会上传到服务器。对于处理敏感文本的场景,这算是个「隐形加分项」。

如果你也经常被全角字符困扰,可以在线体验这个工具:craftvo.app/zh/tool/ful...

最后说一句:在 AI 辅助编程的时代,「会用 AI」和「懂原理」并不矛盾。恰恰是那些你理解原理的地方,才能判断 AI 生成的代码是否靠谱。这波操作,属于是「AI 教我的,但我也教了 AI」了。

相关推荐
xixingzhe25 小时前
本地桌面客户端入门开发
前端
不磕了还不行5 小时前
用 Vue + Element UI 把元宝搜索的返回值做成“能点开检查的证据卡片”
前端·vue.js·ui
Csvn14 小时前
WASM
前端
修己xj15 小时前
我用 AI 给自己写了一个 Markdown 编辑器,然后把它开源了
前端
FRing0315 小时前
vue2干货总结2(v-bind、v-for、v-model、指令修饰符、v-bind操作class和style)
开发语言·前端·vue.js·vue2
幸运小猿16 小时前
模拟慢上传-处理超时重试image 丢失线上问题
前端·javascript·html
moshuying17 小时前
网购能买Token了!
前端
h_654321018 小时前
echarts实现柱状图加一条横线
前端·echarts
风骏时光牛马18 小时前
生成一个后端标题,只需要一个标题,不需要其他的任何文本。
前端
子非鱼@Itfuture18 小时前
关于前后端联调中登录状态/登录信息失效?
java·前端