V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
oldshensheep
V2EX  ›  分享发现

一些语言中 String 的坑

  •  
  •   oldshensheep · 2022-05-12 14:17:13 +08:00 · 2067 次点击
    这是一个创建于 925 天前的主题,其中的信息可能已经有所发展或是发生改变。

    什么 BUG

    这个问题不是 JavaScript 特有,只是记录一下。
    其实就是 String 的长度的问题。String.length 得到的不是字符数

    let str = '哈哈🎋🧨😘';
    console.log(str.length) //8
    
    

    emoji 占了 2 个长度,这个问题在 js 、java 、go 上有,其他语言还没有测试。
    同样的,一些内置函数也受到了这个问题的影响。

    let str = '哈哈🎋🧨😘';
    console.log(str.substring(0, 2)); //哈哈
    console.log(str.substring(0, 3)); //哈哈� //意料之外的结果
    console.log(str.substring(0, 4)); //哈哈🎋
    

    还有 String.charAt()等等
    对于 js 解决办法就是把字符串转换成数组

    let str = '哈哈🎋🧨😘';
    console.log([...str].length) //5
    

    日常开发中可能大家都依赖 String.length ,但是在某些情况下可能会出 BUG ,比如要遍历字符串的时候,当然如果你确保输入的字符串不会有那些特殊字符那就没问题了。

    BUG 现场

    记录一下我遇到的 bug ,还有处理方法。总的来说就是变成数组

    let bug = '哈哈🎋🧨😘';
    
    console.log(bug.length)                             //8
    console.log([...bug].length)                        //5
    
    console.log(bug.split("").reverse().join(""))       //�🗨🮋�哈哈
    console.log([...bug].reverse().join(""))            //😘🧨🎋哈哈
    
    console.log(bug.substring(0, 3));                   //哈哈�
    console.log([...bug].slice(0, 3).join(""));         //哈哈🎋
    
    第 1 条附言  ·  2022-05-12 15:03:59 +08:00
    对于 Javascript 大家不要用我说这个方法了吧……
    [...str]还是会出 bug 可以用#3 楼提供的库,其他语言应该也有类似的。
    主要就是提醒大家编程遍历字符串的时候要注意这个问题。
    15 条回复    2022-05-12 15:29:44 +08:00
    zagfai
        1
    zagfai  
       2022-05-12 14:23:12 +08:00
    字符长度与字节长度是两回事
    DiamondYuan
        2
    DiamondYuan  
       2022-05-12 14:32:18 +08:00   ❤️ 1
    你的处理方法还是会有 bug

    console.log([..."👨‍🚀"].reverse().join(""));
    DiamondYuan
        3
    DiamondYuan  
       2022-05-12 14:33:02 +08:00   ❤️ 2
    oldshensheep
        4
    oldshensheep  
    OP
       2022-05-12 14:40:02 +08:00
    @DiamondYuan 看来还是不行……有点坑人。
    👨‍🚀这个字符我按删除键,变成了另外一个字符……
    pocarisweat
        5
    pocarisweat  
       2022-05-12 14:42:59 +08:00   ❤️ 3
    你可以试试更复杂的情况,比如彩虹旗🏳️‍🌈,这个 emoji 实际上是 2 个 emoji (彩虹+白旗)和中间的连接符拼起来的,在 UTF-16 的情况下长度应该是 6 (比如 JavaScript )。大概只有 Swift 会把它当作一个字符:

    1> "🏳️‍🌈".count
    $R0: Int = 1
    pocarisweat
        6
    pocarisweat  
       2022-05-12 14:44:34 +08:00
    @oldshensheep
    如果你用的是 Firefox ,那按删除键变成另一个字符是 bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1728746
    oldshensheep
        7
    oldshensheep  
    OP
       2022-05-12 14:50:49 +08:00
    @pocarisweat 确实是用 Firefox 。不过 Chrome 的地址栏也有这个问题,要按 3 下才能删除( Firefox 是 2 下),网页中的输入框没问题( Firefox 有问题)。
    rabbbit
        8
    rabbbit  
       2022-05-12 14:51:31 +08:00
    I have a 👨, I have a 🚀. Uh! 👨‍🚀
    rabbbit
        9
    rabbbit  
       2022-05-12 14:53:07 +08:00
    👨‍🚀这个字符是👨 + ZWJ ‍+ 🚀
    ipwx
        10
    ipwx  
       2022-05-12 14:54:44 +08:00
    其实让我比较惊讶的是

    console.log(str.length) //8
    console.log([...str].length) // 5

    别的语言要么都是 8 ,要么都是 5 。不愧是大 JS ,继三位一体不等式以后,又出现了新的不一致性。
    hsfzxjy
        11
    hsfzxjy  
       2022-05-12 14:55:53 +08:00
    说到底还是“字符”的定义产生的混淆。你是想要 code point 还是 grapheme

    https://stackoverflow.com/questions/27331819/whats-the-difference-between-a-character-a-code-point-a-glyph-and-a-grapheme
    ipwx
        12
    ipwx  
       2022-05-12 14:56:31 +08:00
    In [1]: S = '哈哈🎋🧨😘'

    In [2]: S[::-1]
    Out[2]: '😘🧨🎋哈哈'

    In [3]: len(S)
    Out[3]: 5
    xiangyuecn
        13
    xiangyuecn  
       2022-05-12 14:59:09 +08:00
    这个本身就是玄学,单纯肉眼可见的长度,你还得考虑大量的零宽字符:明明就显示的两个字,文件却有 1 个 G 大小

    另外:emoji 有一个连接字符,\u2***多少来着,几个 emoji 连接显示成一个 emoji ,目测可以无限套娃
    billlee
        14
    billlee  
       2022-05-12 15:22:23 +08:00
    ucs-2 的历史遗留问题,java 也有,python 没有
    xtreme1
        15
    xtreme1  
       2022-05-12 15:29:44 +08:00
    大部分的 string 问题都是允许按下标访问
    如果把 string 和 char array 彻底分开就没这些问题,string 只开放迭代器
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   919 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 20:32 · PVG 04:32 · LAX 12:32 · JFK 15:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.