判断字符宽度

  • 判断字符宽度已关闭评论
  • 239 次浏览
  • A+
所属分类:.NET技术
摘要

之前曾在《C# 中容易忽视的 Encoding.GetByteCount 内存问题》中提到过,可以使用 Encoding.Default.GetByteCount 方法来判断字符是全宽(宽度为 2)还是半宽(宽度为 1)。

之前曾在《C# 中容易忽视的 Encoding.GetByteCount 内存问题》中提到过,可以使用 Encoding.Default.GetByteCount 方法来判断字符是全宽(宽度为 2)还是半宽(宽度为 1)。

这个方法实际上是计算对字符编码后产生的字节数,只是在中文环境下,宽字符在使用默认编码后一般都会占用两个字节,使得在大多数场景下都没有什么问题。但根据 Unicode 标准,EAST ASIAN WIDTH 才是准确的字符宽度定义,适用于东亚文本的字符的宽度计算。

标准中定义了六类字符宽度:

  • 全角Fullwidth):在 Unicode 标准中定义的全角字符,例如“0123, ABCD”等。
  • 半角Halfwidth):在 Unicode 标准中定义的半角字符,例如“。「」、・”等。
  • Width):始终为宽的其它字符,一般仅在东亚上下文中出现,例如中文字符。
  • Narrow):始终窄的的其它字符,它们一般都有对应的全角或宽字符,例如“0123, ABCD”等。
  • 不明确Ambiguous):根据不同情况可能显示不同宽窄的字符,
  • 中性Neutral):其它所有字符,一般不用于东亚文本,

标准建议,宽字符包括 W、F 和 A(在东亚上下文中),窄字符包括 N、Na、H 和 A(非东亚上下文中)。下图是一个解析宽度的示例。

判断字符宽度

图 1 字符类别及其解析宽度的示例,图片来自 http://www.unicode.org/reports/tr11/

但在实际测试时发现,A 类别的处理要复杂很多,并非所有 A 类别字符都会显示为宽(或窄),而且不同字体也会有不同的处理方式。

u00A1u00A4u00A7u01CEu22BFu2312 这六个字符为例,它们均为 A 类别,但在 Consolas 字体下,前四个字符显示为窄,后两个字符显示为宽;在新宋体下,第一和第四个字符显示为窄,其它字符显示为宽。

判断字符宽度

图 2 不同字体显示不一致

由于这种场景实在过于复杂,因此目前将 A 类别统一当作窄字符来计算,在用到这类字符的场景下再单独考虑。

将 Unicode 13.0.0 下的 EAST ASIAN WIDTH 统一扫描后,整理得到以下宽字符范围:

# 1100 ~ 115F Hangul Jamo 1100..115F # User interface symbols 231A..231B 2329..232A 23E9..23EC 23F0..23F0 23F3..23F3 25FD..25FE 2614..2615 2648..2653 267F..267F 2693..2693 26A1..26A1 26AA..26AB 26BD..26BE 26C4..26C5 26CE..26CE 26D4..26D4 26EA..26EA 26F2..26F3 26F5..26F5 26FA..26FA 26FD..26FD 2705..2705 270A..270B 2728..2728 274C..274C 274E..274E 2753..2755 2757..2757 2795..2797 27B0..27B0 27BF..27BF 2B1B..2B1C 2B50..2B50 2B55..2B55 # 2E80 ~ 2EFF CJK Radicals Supplement # 2F00 ~ 2FDF Kangxi Radicals # 2FF0 ~ 2FFF Ideographic Description Characters # 3000 ~ 303E CJK Symbols and Punctuation 2E80..303E # 3040 ~ 309F Hiragana # 30A0 ~ 30FF Katakana # 3100 ~ 312F Bopomofo # 3030 ~ 318F Hangul Compatibility Jamo # 3190 ~ 319F Kanbun # 31A0 ~ 31BF Bopomofo Extended # 31C0 ~ 31EF CJK Strokes # 31F0 ~ 31FF Katakana Phonetic Extensions # 3200 ~ 32FF Enclosed CJK Letters and Months # 3300 ~ 33FF CJK Compatibility # 3400 ~ 4DBF CJK Unified Ideographs Extension A 3040..4DBF # 4E00 ~ 9FFC CJK Unified Ideographs # A000 ~ A48F Yi Syllables # A490 ~ A4CF Yi Radicals 4E00..A4CF # A960 ~ A97F Hangul Jamo Extended-A A960..A97F # AC00 ~ D7A3 Hangul Syllables AC00..D7A3 # F900 ~ FAFF CJK Compatibility Ideographs F900..FAFF # FE10 ~ FE19 Vertical forms FE10..FE19 # FE30 ~ FE4F CJK Compatibility Forms # FE50 ~ FE6F Small Form Variants FE30..FE6F # FF00 ~ FF60 Fullwidth ASCII variants FF01..FF60 # FFE0 ~ FFE6 Fullwidth symbol variants FFE0..FFE6 

具体实现可以参见 https://github.com/CYJB/Cyjb/blob/master/Cyjb/CharUtil.cs。