Kotlin/Java - 如何识别全角字符?

7

简短概述:

半角字符:指正常大小的字符。
例如:'A'和'ニ'

全角字符:指在显示屏上需要占用两个等宽英文字符的字符。
例如:'中'、'に' 和 'A'

我需要实现这个函数:

/**
 * @return Is this character a full-width character or not.
 */
fun Char.isFullWidth(): Boolean
{
    // What is the most efficient implementation here?
}

这不是关于字符数据结构的问题,只与显示宽度有关。


长话短说:

我正在重构HyLogger,这是一个以文本颜色渐变为重点的记录库。以下是我遇到的问题:

HyLogger Gradient Problem

如果你看一下屏幕截图中打印的第一个渐变文本块,中间的全宽文本会在之后破坏渐变模式,因为当调用string.length时,尽管它们占据了两倍的空间,但它们被计算为一个字符。

你可能会问,为什么要打印全宽字符?这是一个真正的问题,因为在像中文、日文或韩文这样的语言中,几乎所有字符都是全宽的,因此需要占用两倍的空间,类似于英文的全宽字符。

因此,我需要找到一种方式来识别全宽字符,以便我可以将它们计算为两个渐变像素而不是一个,以解决图片中的问题。


已知信息:

在C ++中检查Unicode字符是否为全宽字符

  1. Unicode网站上有一个东亚宽度字符列表(以及报告),但是每次呈现渐变文本块时遍历整个列表可能不是很有效率。

  2. Python拥有Unicode数据库库,一种可能的解决方案是使用Jython调用Python API,这会很耗费资源,而且效率可能并不高。

在Java中分析全宽或半宽字符

  1. ICU4J库具有实现此功能的Unicode工具,但该库大小为12.5 MB,并不适合我的50 KB记录库。

2
显示宽度取决于字体,因此请选择等宽字体并计算每个字符的宽度。例如,请参考在Java中计算字符串的显示宽度 - Andreas
2
@Andreas 感谢您的建议。但是,Unicode 定义了“半角”和“全角”的标准,在等宽字体中,“半角”字符的宽度将是“全角”字符宽度的一半。而使用 Graphics 来确定单个字符的宽度似乎不太高效。 - Hykilpikonna
3
如果你想查询Java中不可用的Unicode属性,为什么不使用你提供的最后一个链接中第二个回答中提到的库呢?请参考链接:second answer - Andreas
@Andreas 抱歉我没看到。到目前为止,那将是最好的解决方案,但整个库大小为12.5 MB,这对于我的50 KB记录器库来说并不理想。无论如何,谢谢!我会尝试手动导入库的一小部分以供此函数使用。 - Hykilpikonna
1个回答

0

最好的解决方案似乎是将EastAsianWidth.txt转换为一系列范围条件。

enter image description here

下面的函数部分使用{{link1:FullWidthUtilGenerator.kt}}生成,但仍有一些问题需要解决:
  1. 它不考虑基本多文种平面(BMP)范围之外的字符(例如U+10000),因为我还没有找到如何在Java/Kotlin中有效地包含它们。
    \u10000会导致编译错误)

  2. 尚未自动合并在EastAsianWidth.txt中单独说明的接近值。(例如\u3010\u3011

/**
 * Half-width: Regular width characters.
 * Eg. 'A' and 'ニ'
 *
 * Full-width: Chars that take two monospaced English chars' space on the display
 * Eg. '中', 'に' and 'A'
 * 
 * See FullWidthUtilGenerator.kt
 *
 * @return Is this character a full-width character or not.
 */
fun Char.isFullWidth(): Boolean
{
    return when (this)
    {
        '\u2329','\u232A','\u23F0','\u23F3','\u267F','\u2693','\u26A1','\u26CE','\u26D4','\u26EA','\u26F5',
        '\u26FA','\u26FD','\u2705','\u2728','\u274C','\u274E','\u2757','\u27B0','\u27BF','\u2B50','\u2B55',
        '\u3000','\u3004','\u3005','\u3006','\u3007','\u3008','\u3009','\u300A','\u300B','\u300C','\u300D',
        '\u300E','\u300F','\u3010','\u3011','\u3014','\u3015','\u3016','\u3017','\u3018','\u3019','\u301A',
        '\u301B','\u301C','\u301D','\u3020','\u3030','\u303B','\u303C','\u303D','\u303E','\u309F','\u30A0',
        '\u30FB','\u30FF','\u3250','\uA015','\uFE17','\uFE18','\uFE19','\uFE30','\uFE35','\uFE36','\uFE37',
        '\uFE38','\uFE39','\uFE3A','\uFE3B','\uFE3C','\uFE3D','\uFE3E','\uFE3F','\uFE40','\uFE41','\uFE42',
        '\uFE43','\uFE44','\uFE47','\uFE48','\uFE58','\uFE59','\uFE5A','\uFE5B','\uFE5C','\uFE5D','\uFE5E',
        '\uFE62','\uFE63','\uFE68','\uFE69','\uFF04','\uFF08','\uFF09','\uFF0A','\uFF0B','\uFF0C','\uFF0D',
        '\uFF3B','\uFF3C','\uFF3D','\uFF3E','\uFF3F','\uFF40','\uFF5B','\uFF5C','\uFF5D','\uFF5E','\uFF5F',
        '\uFF60','\uFFE2','\uFFE3','\uFFE4',
        in '\u1100'..'\u115F',in '\u231A'..'\u231B',in '\u23E9'..'\u23EC',in '\u25FD'..'\u25FE',
        in '\u2614'..'\u2615',in '\u2648'..'\u2653',in '\u26AA'..'\u26AB',in '\u26BD'..'\u26BE',
        in '\u26C4'..'\u26C5',in '\u26F2'..'\u26F3',in '\u270A'..'\u270B',in '\u2753'..'\u2755',
        in '\u2795'..'\u2797',in '\u2B1B'..'\u2B1C',in '\u2E80'..'\u2E99',in '\u2E9B'..'\u2EF3',
        in '\u2F00'..'\u2FD5',in '\u2FF0'..'\u2FFB',in '\u3001'..'\u3003',in '\u3012'..'\u3013',
        in '\u301E'..'\u301F',in '\u3021'..'\u3029',in '\u302A'..'\u302D',in '\u302E'..'\u302F',
        in '\u3031'..'\u3035',in '\u3036'..'\u3037',in '\u3038'..'\u303A',in '\u3041'..'\u3096',
        in '\u3099'..'\u309A',in '\u309B'..'\u309C',in '\u309D'..'\u309E',in '\u30A1'..'\u30FA',
        in '\u30FC'..'\u30FE',in '\u3105'..'\u312F',in '\u3131'..'\u318E',in '\u3190'..'\u3191',
        in '\u3192'..'\u3195',in '\u3196'..'\u319F',in '\u31A0'..'\u31BF',in '\u31C0'..'\u31E3',
        in '\u31F0'..'\u31FF',in '\u3200'..'\u321E',in '\u3220'..'\u3229',in '\u322A'..'\u3247',
        in '\u3251'..'\u325F',in '\u3260'..'\u327F',in '\u3280'..'\u3289',in '\u328A'..'\u32B0',
        in '\u32B1'..'\u32BF',in '\u32C0'..'\u32FF',in '\u3300'..'\u33FF',in '\u3400'..'\u4DBF',
        in '\u4E00'..'\u9FFC',in '\u9FFD'..'\u9FFF',in '\uA000'..'\uA014',in '\uA016'..'\uA48C',
        in '\uA490'..'\uA4C6',in '\uA960'..'\uA97C',in '\uAC00'..'\uD7A3',in '\uF900'..'\uFA6D',
        in '\uFA6E'..'\uFA6F',in '\uFA70'..'\uFAD9',in '\uFADA'..'\uFAFF',in '\uFE10'..'\uFE16',
        in '\uFE31'..'\uFE32',in '\uFE33'..'\uFE34',in '\uFE45'..'\uFE46',in '\uFE49'..'\uFE4C',
        in '\uFE4D'..'\uFE4F',in '\uFE50'..'\uFE52',in '\uFE54'..'\uFE57',in '\uFE5F'..'\uFE61',
        in '\uFE64'..'\uFE66',in '\uFE6A'..'\uFE6B',in '\uFF01'..'\uFF03',in '\uFF05'..'\uFF07',
        in '\uFF0E'..'\uFF0F',in '\uFF10'..'\uFF19',in '\uFF1A'..'\uFF1B',in '\uFF1C'..'\uFF1E',
        in '\uFF1F'..'\uFF20',in '\uFF21'..'\uFF3A',in '\uFF41'..'\uFF5A',in '\uFFE0'..'\uFFE1',
        in '\uFFE5'..'\uFFE6' -> true
        else -> false
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接