V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Richard14
V2EX  ›  问与答

各个语言底层储存 String 字符串的方式?

  •  
  •   Richard14 · 2022-06-06 21:06:03 +08:00 · 880 次点击
    这是一个创建于 891 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天想到一个问题,字符串应该是一种虽然常用,但比我们想象的更复杂的数据结构。比如新人初学 C++时应该都会有 input 一个什么东西然后看 output 的教学。但是实际上字符串首先是可变长的,意味着没法在编译时固定长度,第二是它展示的是高层信息,比如中文字符串底层(假设按照 utf-8 存储)显然并不能直接阅读,如果考虑 utf-8 的问题的话它还涉及变长问题,也就是不能认为每个字符的物理信息就是 1byte ,因为实际上可能在 1-4 之间不一定是多少,如果考虑世界上不同语言,中东似乎还有从右往左的语言,这在一些特定环境下应该也会需要额外的代码处理。。

    所以平常很长用的字符串切片操作,比如对一个字符串,slice 第 10 到第 20 个字组成的子串,各语言是如何做到高效定位该从哪里开始从哪里结束的?比如我有一个四百万字的字符串,需要 slice 第三百九十九万字以后的内容, 要确定第 399 万个字从哪里开始岂不是需要从 0 开始遍历才能准确定位,这样一来效率岂不会很低吗?实际日常使用似乎并没有类似的感觉,有哪些优化吗?

    6 条回复    2022-06-07 23:56:43 +08:00
    ysc3839
        1
    ysc3839  
       2022-06-06 21:12:08 +08:00 via Android
    一般是用定长(UTF-32)或者大部分定长(UTF-16)的编码来操作
    nightwitch
        2
    nightwitch  
       2022-06-06 21:26:43 +08:00
    从右往左的语言这是排版问题,与字符串无关。

    UTF-16 在基本平面(BMP)内的字符可以用双字节表示,所以这部分是定长的,基本平面能表示一些语言的常用字符。微软的所谓宽字符即是指这部分编码,不考虑需要用代理对表示的字符,在处理基本平面外的字符会返回错误的结果。比如`𪚥`字符,Windows API 会认为其为两个汉字。

    如果你要非常准确的 slice 第 10 到第 20 个字组成的子串,且要在复杂度 O(1 )内完成,可以用 UTF-32 存储,这样每个字用四个字节存储,算是空间换时间。
    acehowxx
        3
    acehowxx  
       2022-06-06 21:48:36 +08:00 via Android
    go 语言是按 utf-8 的 byte 数组来存字符串。
    mcfog
        4
    mcfog  
       2022-06-07 08:56:37 +08:00
    https://stackoverflow.com/questions/27331819/whats-the-difference-between-a-character-a-code-point-a-glyph-and-a-grapheme

    是这样的,字符串有复杂性或者说歧义是因为“字符”这个概念在排版、计算机、日常生活中的概念并不完全一致

    这件事情上来说我最喜欢的处理是 php 的办法,php 对程序员撒了一个“可爱的谎”说 string 其实是[]byte ,所有的 str*操作其实都是二进制[]byte 操作,然后提供 mb*用来操作内容为 unicode 的 string ,非常舒服。
    billlee
        5
    billlee  
       2022-06-07 11:19:04 +08:00 via Android
    C/C++: 字节数组,怎么用自己看着办
    Python: UCS-4 定长
    Java: UTF-16 变长

    用 UTF-16 的很可能是 UCS-2 定长编码的历史遗留问题

    一般不存在直接取三百九十九万字的场景,你不检查前面的内容是什么,怎么知道要从什么地方开始截取呢
    ecnelises
        6
    ecnelises  
       2022-06-07 23:56:43 +08:00
    1. UTF-8 是变长的,UTF-16 在有代理对的情况下也是变长的,得 UTF-32 才行

    2. 即使考虑了变长情况,UTF-8/UTF-16 编码的一个「字符」还不是我们理解里的字符,emoji 这里尤其复杂:比如国旗🇨🇳实际上是由两个从 U+1F1E6 到 U+1F1FF 的特殊字符拼成的。

    3. 如果要在显示的层面考虑「字符」( glyph )就更复杂了,因为几个可以单独存在的字符挨在一起时可以拼到一块,比如某些特殊语言或者 ligature

    所以用 UTF-16 算是一个相对折中的方案,如果你不严格要求从第 390 万个「字符」开始,可以先跳到第 390 万个位置上,然后根据当前位置是 Higher/Lower Part 做调整
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3336 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 105ms · UTC 12:02 · PVG 20:02 · LAX 04:02 · JFK 07:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.