2 字节的 char 数据类型不足以处理 Unicode 字符串中的“字符”概念吗?

2024-01-07

各种编程语言都使用 2 字节char数据类型(不要与 C/C++ 的数据类型混淆)char,这只是一个字节),字符串是由它构造的。各种实用函数会尝试找到这样的char在一个字符串中,就像寻找一个e in hello,或者进行其他接受或返回的操作chars(分割、索引、替换、计算字符串中字符出现的次数、长度……)。

如果您深入挖掘,您会发现有关 Unicode 代码点的信息。事实上,Java(我假设还有其他语言)允许您迭代这些代码点。但这些似乎是由一个int(4字节)不是char(2 个字节)。您很少会看到人们使用代码点来迭代字符串。由于这样的代码点可能跨越多个chars(最多 2,对吧?int?)这不是进行字符串操作的最快方法,但它似乎是正确的方法。

某些程序/框架/操作系统(?)也将无法在多操作系统下正常工作char字符,而不是只删除第二个char并创造一个“腐败”的角色。

在处理字符串时,您不应该始终使用对代码点进行操作的方法吗?我缺少什么?恐怕必须有人向我解释为什么世界一直在使用char当这看起来已经过时时。 char 的大小到底够用吗?我知道还有额外的“帮助”字符用于“升级”其他字符(将 o 变成 ö 等)。这些是如何处理的char和代码点迭代?如果你更换的话,是不是有机会严重损坏你的字符串chars 而不是“整个”代码点?


总结一下问题的答案

2 字节 char 数据类型不足以处理 Unicode 字符串中的“字符”概念吗?

is 是的,它不足以存储 Unicode 字符,但您无需担心,因为您不使用也不应该使用它来迭代

See also

  • 16 位 wchar_t 是否正式有效用于表示完整的 Unicode? https://stackoverflow.com/q/39548465/995714
  • C++ wchar_t 和 wstrings 有什么“问题”?宽字符有哪些替代方案? https://stackoverflow.com/q/11107608/995714

欲了解更多详情,请阅读下文


在处理字符串时,您不应该始终使用对代码点进行操作的方法吗?

人们几乎不应该这样做,因为与普遍的看法相反,UTF-32 中的字符not有固定长度。 UTF-32 只是一种固定长度编码单个代码点, but a 用户感知的角色可以由多个代码点表示:

重要的是要认识到,用户所认为的“字符”(语言书写系统的基本单位)可能不仅仅是单个 Unicode 代码点。相反,该基本单元可能由多个 Unicode 代码点组成。为了避免计算机使用术语“字符”产生歧义,这称为“用户感知的字符”。例如,“G”+ 重音符号是用户感知的字符:用户将其视为单个字符,但实际上由两个 Unicode 代码点表示。这些用户感知的字符通过所谓的字素簇来近似,可以通过编程方式确定。

字素簇边界对于排序规则、正则表达式、UI 交互、垂直文本分割、首字母样式边界识别以及文本中“字符”位置的计数非常重要。

Unicode 文本分割 - 字素簇边界 https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries

所以我们应该只使用字素 A.K.A用户感知的角色相反,并且不得将段分解为代码点。例如,人们通常会迭代字符串来查找特定字符,如果我们想找到稻穗????U+1F33E那么它会意外地匹配????‍????,因为农民表情符号被编码为U+1F468 U+200D U+1F33E。然后该索引可用于将子字符串从 ???? 转移到其他内容,这可能会让用户大吃一惊。看为什么像 ????‍????‍????‍???? 这样的表情符号在 Swift 字符串中的处理方式如此奇怪? https://stackoverflow.com/q/43618487/995714

另一个常见的错误是人们将字符串截断到第一个或最后一个N字符和附加/前置"..."当它太长时将其放入 UI,然后它会严重崩溃,因为char在索引处N可能位于字素簇的中间。例如"????‍????‍????‍????????‍????️????????‍❤️‍????‍????????"是一个不太长的字符串3个用户感知的角色但它是由21个码位因此,如果您在第 20 个字符处截断,那么它会完全弄乱输出字符串。或者检查印度语字符串"ফোল্ডার"可以很容易地看出有4 个字符通过使用鼠标或箭头键选择或迭代它(尽管我必须承认我不是任何印度语言的专家),但它被编码为7 个代码点 (U+09AB U+09CB U+09B2 U+09CD U+09A1 U+09BE U+09B0)并且在中间被截断时会表现得很糟糕。如果不考虑多代码点字符,反转字符串或查找回文将具有相同的命运

印度语和阿拉伯语(卡纳达语、孟加拉语、泰语、缅甸语、老挝语、马来语、印地语、波斯语、阿拉伯语、泰米尔语...)大量使用ZWJ https://en.wikipedia.org/wiki/Zero-width_joiner and ZWNJ https://en.wikipedia.org/wiki/Zero-width_non-joiner修改字符。在这些语言中,当没有 ZWJ 时,字符也会相互组合或相互修改,如前面的示例字符串所示。其他一些例子:நி (U+0BA8 U+0BBF), षि (U+0937 U+093F)。如果删除中间的代码点或获取子字符串,则它可能无法按预期工作。许多语言,如缅甸语、蒙古语、CJKV...以及数学符号和表情符号也使用变化 https://en.wikipedia.org/wiki/Variation_Selectors_%28Unicode_block%29 选择器 https://en.wikipedia.org/wiki/Variation_Selectors_Supplement (VS https://stackoverflow.com/q/4974668/995714) 调整前一个字符。例如က︀ (U+1002 U+FE00)、ဂ︀ (U+1000 U+FE00)、င︀ (U+1004 U+FE00)、⋚︀ (U+22DA U+FE00)、丸︀ (U +4E38 U+FE00)。这是替代变体的完整列表 https://unicode.org/Public/UCD/latest/ucd/StandardizedVariants.txt。删除 VS 将更改文档的呈现,这可能会影响含义或可读性。您不能在国际化应用程序中任意轻松地获取子字符串

您可以查看书写系统和 Unicode 简介 - 复杂脚本渲染 https://r12a.github.io/scripts/tutorial/part3 and 复杂的文本布局 https://en.wikipedia.org/wiki/Complex_text_layout如果您对有关这些脚本的更多信息感兴趣

有些人提到了使用组合字符 https://en.wikipedia.org/wiki/Combining_character像 g̈ (U+0067 U+0308), Å (U+0041 U+030A) 或 é (U+0065 U+0301),但这只是一个tiny不常见的用例,其中一个字符由多个代码点表示,并且通常可转换为预制字符 https://en.wikipedia.org/wiki/Precomposed_character。在许多其他语言中,这种组合序列更为常见,并且不利于文本的呈现。我将举一些例子[]以及中规定的一些规则Unicode 文本分割 https://unicode.org/reports/tr29/:

  • Do not break Hangul syllable sequences.
    • [ 在韩语中,字符可以由以下组成:Jamos https://en.wikipedia.org/wiki/List_of_Hangul_jamo: 훯 (U+D6E0 U+11B6), 가 (U+1100 U+1161), 각 (U+1100 U+1161 U+11A8), 까ᇫ (U+1101 U+1161 U+11EB)。除了一些奇怪的标准 https://devblogs.microsoft.com/oldnewthing/20201009-00/?p=104351 ]
  • Do not break before extending characters or ZWJ.
    • [ 例如印度字符,如 ൺ (U+0D23 U+0D4D U+200D), ല്‍ (U+0D32 U+0D4D U+200D), ര്‍ (U+0D30 U+0D4D U+200D), क्‍ (U+0915 U+094D U+200D) ]
  • Do not break within emoji modifier sequences or emoji zwj sequences.
    • [ ????????‍♀️ (U+1F3C3 U+1F3FB U+200D U+2640 U+FE0F), ????????‍♀️ (U+1F3C3 U+1F3FF U+200D U+2640 U+FE0F), ????‍????‍????‍???? (U+1F469 U+200D U+1F469 U+200D U+1F466 U+200D U+1F466), ????‍????‍????‍???? (U+1F468 U+200D U+1F469 U+200D U+1F466 U+200D U+1F466), ????‍????️ (这是一个超宽表情符号,而不是两个,由 U+1F636 U+200D U+1F32B U+FE0F 组合而成), ????‍❤️‍???? (U+1F468 U+200D U+2764 U+FE0F U+200D U+1F468), ????????‍❤️‍???????? (U+1F469 U+1F3FC U+200D U+2764 U+FE0F U+200D U+1F468 U+1F3FD), ????????‍❤️‍????‍???????? (U+1F469 U+1F3FB U+200D U+2764 U+FE0F U+200D U+1F48B U+200D U+1F469 U+1F3FF), ????‍???? (U+1F431 U+200D U+1F680), ????‍???? (U+1F431 U+200D U+1F464), ????‍???? (U+1F431 U+200D U+1F409), ????‍???? (U+1F431 U+200D U+1F4BB), ????‍???? (U+1F431 U+200D U+1F453), ????‍???? (U+1F431 U+200D U+1F3CD), ???????? (U+1F467 U+1F3FB), ???????? (U+1F935 U+1F3FB), ❤️ (U+2764 U+FE0F), 1️⃣ (U+0031 U+FE0F U+20E3), ⚕️ (U+2695 U+FE0F), ©️ (U+00A9 U+FE0F), ®️ (U+00AE U+FE0F), ‼️ (U+203C U+FE0F), ™️ (U+2122 U+FE0F), ☑︎ (U+2611 U+FE0E), ????‍☠ (U+1F3F4 U+200D U+2620 U+FE0F), ????️‍⚧ (U+1F3F3 U+FE0F U+200D U+26A7 U+FE0F), ????️‍???? (U+1F3F3 U+FE0F U+200D U+1F308)]。注意:上述某些表情符号可能无法在您的系统上正确显示,因为它们是特定于平台的
  • Do not break within emoji flag sequences. That is, do not break between regional indicator (RI) symbols if there is an odd number of RI characters before the break point.
    • [ 州/地区标志通常由 2 个地区指示符号创建,例如 ???????? (U+1F1FB U+1F1F3), ???????? (U+1F1FA U+1F1F8), ???????? (U+1F1EC U+1F1E7), ???????? (U+1F1EF U+1F1F5), ???????? (U+1F1E9 U+1F1EA), ???????? (U+1F1EB U+1F1F7), ???????? (U+1F1EA U+1F1FA), ???????? (U+1F1FA U+1F1F3)]。注意:您可能只会看到这些字母,尤其是在 Windows 上,因为微软以某种方式拒绝将标志表情符号添加到他们的平台上

字素簇边界规则 https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules


恐怕有人必须向我解释为什么世界仍在使用 char,而这似乎已经过时了。

正如所说,没有人应该迭代chars 在字符串中,无论是否char长度为 1、2 或 4 字节。这最正确的方法是迭代字素。像 ICU 这样的优秀 Unicode 库会对您有所帮助。上面的Unicode文档中也提到了这一点

就用户而言,文本的底层表示并不重要,但重要的是编辑界面呈现用户所认为的字符的统一实现。默认情况下,对于诸如首字下沉格式设置以及文本选择、箭头键移动或文本退格等操作的实现,字素簇可以被视为单位。例如,当字素簇在内部由由基本字符 + 重音符号组成的字符序列表示时,使用右箭头键将从基本字符的开头跳到最后一个重音符号的末尾。

Unicode 文本分割 - 字素簇边界 https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries

不幸的是,在许多情况下,由于缺乏适当的 Unicode 库,这是不可能的,所以在这种情况下,人们可以迭代代码点相反,但他们需要小心避免匹配或剪切字素中间的字符串,从而破坏用户感知的字符

事实上,许多现代语言通过使用通常称为的类型来阻止您迭代字符串中的字节"rune" https://stackoverflow.com/q/19310700/995714相反,它实际上是 UTF-32,并且避免经典char完全地或者只是将其作为遗留类型。例如,在 Go 中我们有rune https://go.dev/blog/strings在 C# 中有System.Text.Rune https://learn.microsoft.com/en-us/dotnet/api/system.text.rune?view=net-5.0。在生锈时strings https://doc.rust-lang.org/book/ch08-02-strings.html are 以 UTF-8 格式存储 https://doc.rust-lang.org/std/string/struct.String.html but 迭代 https://doc.rust-lang.org/book/ch08-02-strings.html#methods-for-iterating-over-strings完成于char https://doc.rust-lang.org/std/primitive.char.html(它代表 Unicode 标量值而不是byte):

for b in "नमस्ते".bytes() {
    println!("{}", b);
}

python3 中的循环以类似的方式完成:

>>> s = "abc\u20ac\U00010302\U0010fffd"
>>> len(s)
6
>>> for c in s:
...     print('U+{:04X}'.format(ord(c)))
...     
U+0061
U+0062
U+0063
U+20AC
U+10302
U+10FFFD

可以看出,你永远不会循环遍历每个byte在他们中。字符串迭代是在符文上完成的,因此底层字符串编码完全不相关。实现可以使用 UTF-8、UTF-16、UTF-32 或任何 Unicode 编码,但用户仍然对此一无所知,因为他们只与符文交互。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

2 字节的 char 数据类型不足以处理 Unicode 字符串中的“字符”概念吗? 的相关文章

  • strtok - 如何避免换行并放入字符串数组?

    如果我欺骗了主题 我真的很抱歉 我在这里搜索但没有结果 我有代码 void split char str char splitstr char p char splitbuf 32 int i 0 p strtok str while p
  • 将 time.Time 转换为字符串

    我正在尝试将数据库中的一些值添加到 string在围棋中 其中一些是时间戳 我收到错误 无法在数组元素中使用 U Created date 类型 time Time 作为类型字符串 我可以转换吗time Time to string typ
  • 如何在 SVG 中显示 unicode?

    以 SVG 格式存储在数据库中的信息 如果数据包含文本 它将显示为 Unicode 有必要在浏览器中正确显示 SVG 文件
  • 在FLUTTER/DART中,为什么我们有时在声明变量时要在“String”后面加一个问号?

    在演示应用程序中 我们找到一个实例 最终字符串 标题 gt 为什么要加这个 在 String 类型之后 class MyHomePage extends StatefulWidget MyHomePage Key key this titl
  • XSL字符串多重替换功能

    如何让这个函数进行多重替换 经验 替换aaa with 111并替换bbb with 222 etc
  • 如何处理最终字符串?

    制作有什么好处吗String as final或者我们可以做String as final 我的理解是 由于 String 是不可变的 因此没有必要将其设为最终的 这是正确的还是人们想要的情况String as Final Code pri
  • 清洁琴弦的更好方法?

    我正在使用这种方法来清理字符串 public static string CleanString string dirtyString string removeChars lt gt string result dirtyString f
  • CharInSet 不适用于非英文字母?

    我已经将应用程序从 Delphi 2007 更新到 Delphi 2010 一切都很顺利 除了一条编译正常但不起作用的语句 If Edit1 Text 1 in S then ShowMessage Found else ShowMessa
  • Python - Unicode 到 ASCII 的转换

    我无法在不丢失数据的情况下将以下 Unicode 转换为 ASCII u ABRA xc3O JOS xc9 I tried encode and decode他们不会这么做 有人有建议吗 Unicode 字符u xce0 and u xc
  • 为什么 re.findall 在查找字符串中的三元组项时不具体。 Python

    所以我有四行代码 seq ATGGAAGTTGGATGAAAGTGGAGGTAAAGAGAAGACGTTTGA OR 0 re findall r ATG 9 TAA TAG TGA seq 首先让我解释一下我正在尝试做什么 如果这令人困惑
  • 在 C# 中将 ANSI (Windows 1252) 转换为 UTF8

    I ve 之前问过这个 https stackoverflow com q 4351985 398713之前在 Stack Overflow 上以一种迂回的方式 这次想把它做好 如何将 ANSI 代码页 1252 转换为 UTF 8 同时保
  • .NET 字符串.替换

    我很生气 通常 我喜欢像 C 中那样进行替换 但是是否有一种 C 风格的替换 它一次仅替换一个字母或我指定的 X 数量 不 BCL 中不存在仅替换字符的单个实例的 Replace 方法 两个主要的 Replace 方法将替换所有出现的情况
  • 如何从 JavaScript 调用 ASSX?

    我想调用一个 ASHX 文件并从 JavaScript 传递一些查询字符串变量 并将返回字符串获取到 JavaScript 中的字符串中 我该怎么做 ASHX 文件已被编码为response write 一个基于查询字符串的字符串 像这样的
  • 防止字符串中出现西里尔文/希腊文/中文 - C# 4.0

    我们有一个支持希腊语 西里尔语 中文字符的系统 使用 ASP NET C 4 0 但第三方系统似乎无法正常工作 为了避免为此第三方系统输入数据时出现问题 我想将文本字段限制为仅接受英语或重音字符 但返回其他字符的验证错误 我怎样才能做到这一
  • 如何使用JavaScript估算字符串的磁盘大小?

    我需要尝试估计DISKJavaScript 中文本字符串 可以是原始文本或图像 音频 等的 Base64 编码字符串 的大小 我不知道如何估计这个 当谷歌搜索时我唯一能找到的是 length所以我想 StackOverflow 上也许有人知
  • 递归检查字符串中的所有字母是否都是大写

    我必须检查递归中所有字母是否都是大写字母 我不知道为什么这不起作用 public static bool IsCapital string str if str Length 1 return int Parse str 0 ToStrin
  • 使用 pygame 显示 unicode 符号

    我检查了其他答案 但不明白为什么我的代码错误地显示 This is what I currently see https i stack imgur com 8tNIK png 这是关于文本渲染的相关代码 font pygame font
  • 根据列中的部分字符串匹配选择数据框行

    我想根据列中字符串的部分匹配从数据框中选择行 例如列 x 包含字符串 hsa 使用sqldf if它有一个like语法 我会做类似的事情 select from lt gt where x like hsa 很遗憾 sqldf不支持该语法
  • 如何在Python中按字母顺序对字符串中的字母进行排序

    有没有一种简单的方法可以在Python中按字母顺序对字符串中的字母进行排序 So for a ZENOVW 我想返回 ENOVWZ 你可以做 gt gt gt a ZENOVW gt gt gt join sorted a ENOVWZ
  • 为不带引号的函数获取字符串参数

    我有一个函数 用于从 URL 下载文件并将其写入磁盘 并施加特定的文件扩展名 目前 它看起来像这样 import requests import os def getpml url filename psc requests get url

随机推荐

  • 动态加载 JavaScript 文件

    如何可靠且动态地加载 JavaScript 文件 这可用于实现一个模块或组件 当 初始化 时 该组件将根据需要动态加载所有需要的 JavaScript 库脚本 使用该组件的客户端不需要加载所有库脚本文件 并手动插入
  • Node.js、Express.js - 意外标记 {

    我的应用程序每次到达此行时都会崩溃 const name price req query 似乎无法找到确切的答案 这是错误日志 SyntaxError Unexpected token at exports runInThisContext
  • jsPDF 分页符

    我的页面 tab1 和 tab2 中有 2 个 div 我想将两个 div 导出到 1 个 PDF 文件中 其中 tab1 作为第一页 tab2 从下一页开始 目前 第二个 div 在导出的文件中出现损坏 因此我希望该 div 从下一页出现
  • 如何使用specs2对测试进行分组?

    我习惯了 JUnit 在 JUnit 中 只需在单个文件 类 中定义这些测试并用 Test 然后 为了运行其中几个测试 TestSuite是用创建的 Suite SuiteClasses等等 在specs2中 可以将多个测试分组在两个不同的
  • iOS – UIAppearance外观WhenContainedIn问题

    我正在为导航栏设置图像 如下所示 UINavigationBar appearance setBackgroundImage UIImage imageNamed navbar png forBarMetrics UIBarMetricsD
  • Gmail API users.watch - 没有历史记录的详细信息

    我已成功设置 Google Pub Sub 以使用 Gmail API Watch 功能 如下所述 https developers google com gmail api guides push https developers goo
  • crt1.o:在函数 `_start' 中: - Linux 中对 `main' 的未定义引用

    我正在将应用程序从 Solaris 移植到 Linux 链接的目标文件没有定义 main 但编译和链接在 Solaris 中正确完成 并且生成了可执行文件 在 Linux 中我收到此错误 usr lib gcc x86 64 redhat
  • 同步调用异步 Javascript 函数

    首先 这是一种非常具体的情况 故意以错误的方式将异步调用改造为非常同步的代码库 该代码库有数千行长 而且时间目前没有能力进行更改以 执行对的 它伤害了我的每一根神经 但现实和理想常常不一致 我知道这很糟糕 好吧 顺便说一句 我该如何做到这一
  • 如何使用 IAIK JCE 在 Java 中使用 PKCS#5 格式的 PBE 加密 RSA 私钥?

    我已经创建了 RSA 密钥对 现在 我尝试使用 DES 算法加密私钥 将其格式化为 PKCS 5 并将其打印在控制台上 不幸的是 生成的私钥不起作用 当我尝试使用它时 输入后right密码短语 ssh 客户端返回密码短语无效 加载密钥 te
  • iPhone 在延迟后取消选择行

    我想知道是否有人知道延迟后取消选择表视图的方法 我正在使用deselectRowAtIndexPath方法 我只想在取消选择之前突出显示一秒钟 Thanks 我能够使用 tableView deselectRowAtIndexPath in
  • JavaFX:在 ButtonBar 内对齐按钮(使用 SceneBuilder 或 fxml)

    我有一个 JavaFXButtonBar http docs oracle com javase 8 javafx api javafx scene control ButtonBar html有两个按钮 通过 SceneBuilder 创
  • 如何手动卸载openerp模块

    我在 openerp v7 上安装了一个模块 我想卸载它 使用界面失败 我在卸载过程中收到错误 是否有 手动 方式来卸载模块 删除下面的模块文件夹就足够了吗addons 还是还有其他事情要做 以最干净的方式做到这一点 这是当我尝试通过界面卸
  • 使用 Tkinter GUI 中的 read_csv 打开并读取 csv 文件

    from tkinter import filedialog Label Button Entry StringVar from tkinter filedialog import askopenfile import pandas as
  • 在 Samsung Galaxy S2 上使用 Android MediaRecorder 录制的视频已损坏

    再会 我正在学习如何使用 MediaRecorder 录制视频 但播放时录制的视频已损坏 看这个截图 http www 4shared com photo QtmJCHRi corrupted video html http www 4sh
  • 如何使用 Magick.Net 调整 jpeg 质量

    我正在尝试将彼此附加的两张图像的图像质量设置为 10 并将图像大小调整为 40x40 using var images new MagickImageCollection designFile swatchFile MagickImage
  • 在多线程程序中同步嵌入式Python

    以下是在多线程程序中使用Python解释器的示例 include
  • 如何在 RethinkDB 中通过对象数组查询多索引

    我正在使用一个看起来像这样的数据集 bitrates format mp3 rate 128K format aac rate 192K details id 1 name For Those About To Rock We Salute
  • 定期付款给已经运行的贝宝脚本

    我看过很多相关问题 但没有找到任何答案 我已成功通过 PayPal 实施交易 但现在要求我通过 PayPal 实施定期付款 我进行了搜索 但找不到任何可以帮助我实现此目的的材料 首先是我已经实施的 我要求用户选择金额 用户选择金额 我将用户
  • Maven 默认的打包类型有哪些?

    我似乎无法在网上或 Maven 文档中找到这个 我想知道 Maven 3 支持哪些开箱即用的打包类型 有测试包吗 谢谢 麻烦您了 当前的核心包装价值是 pom jar Maven 插件 ejb war ear rar par https m
  • 2 字节的 char 数据类型不足以处理 Unicode 字符串中的“字符”概念吗?

    各种编程语言都使用 2 字节char数据类型 不要与 C C 的数据类型混淆 char 这只是一个字节 字符串是由它构造的 各种实用函数会尝试找到这样的char在一个字符串中 就像寻找一个e in hello 或者进行其他接受或返回的操作c