• 判断字符宽度


    之前曾在《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 类别字符都会显示为宽(或窄),而且不同字体也会有不同的处理方式。

    \u00A1\u00A4\u00A7\u01CE\u22BF\u2312 这六个字符为例,它们均为 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。

  • 相关阅读:
    numpy学习之矩阵之旅
    HDFS之深入简出(一)
    Hadoop特点
    ThreadLocal的理解
    DBCP连接数据库了解一下
    DBCP连接数据库了解一下
    axaj 的回调
    javaWEB登录ajax传值
    JavaWeb网站后台开发记录手册
    Python--控制循环语句
  • 原文地址:https://www.cnblogs.com/cyjb/p/CharWidth.html
Copyright © 2020-2023  润新知