| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
共有 2331 人关注过本帖
标题:判断一个字符是否纯汉字(非标点符号、英数等)的函数
只看楼主 加入收藏
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:10899
专家分:43509
注 册:2014-5-20
收藏(1)
得分:0 
用下面的 IsUTF8Chinese() 检测 0x4E00 ~ 0x9FFF 的UTF-8直接判断好像未见异常。
程序代码:
** 如果字符表达式的前3个字节是一个UTF-8汉字字符,则返回真(.T.)
FUNCTION IsUTF8Chinese(cStr)
    LOCAL bytes
    bytes = LEFT(cStr,3)
    RETURN LEN(bytes)==3 AND;
        BETWEEN(SUBSTR(bytes,1,1),0hE4,0hE9) AND;
        BETWEEN(SUBSTR(bytes,2,1),0h80,0hBF) AND;
        BETWEEN(SUBSTR(bytes,3,1),0h80,0hBF) 
ENDFUNC

生成的对照表
图片附件: 游客没有浏览图片的权限,请 登录注册

图片附件: 游客没有浏览图片的权限,请 登录注册

测试代码:
程序代码:
SET DEFAULT TO (ADDBS(JUSTPATH(SYS(16))))
STRTOFILE("", "utf16.txt", 2)
STRTOFILE("", "utf8.txt",  4)
err = .f.
utf16 = STRCONV("0x4E00",5)
utf8  = STRCONV("0x4E00",9)
n = 0
FOR i=0x4E00 TO 0x9FFF
    int_16 = IIF(i<0x8000, i, i-2^16)
    utf16_chinese = BINTOC(int_16,"2rs")
    utf8_chinese = STRCONV(utf16_chinese,10)
    IF !IsUTF8Chinese(utf8_chinese)
        err = .t.
        EXIT 
    ENDIF
    n = n+1
    utf16 = utf16 + 0h2000 + utf16_chinese
    utf8  = utf8  + 0h20   + utf8_chinese
    IF n%32 == 0
        STRTOFILE(utf16+0h0D000A00, "utf16.txt", 1)
        STRTOFILE(utf8+0h0D0A,      "utf8.txt",  1)
        utf16 = STRCONV("0x"+RIGHT(TRANSFORM(i+1,"@0"),4),5)
        utf8  = STRCONV(utf16,10)
        n = 0
    ENDIF
ENDFOR
IF !err
    RUN notepad utf16.txt
    RUN notepad utf8.txt
ELSE
    ? "error  UTF-16: "+TRANSFORM(i,"@0"), "  UTF-8: "+STRCONV(utf8_chinese,15)
ENDIF

** 如果字符表达式的前3个字节是一个UTF-8汉字字符,则返回真(.T.)
FUNCTION IsUTF8Chinese(cStr)
    LOCAL bytes
    bytes = LEFT(cStr,3)
    RETURN LEN(bytes)==3 AND;
        BETWEEN(SUBSTR(bytes,1,1),0hE4,0hE9) AND;
        BETWEEN(SUBSTR(bytes,2,1),0h80,0hBF) AND;
        BETWEEN(SUBSTR(bytes,3,1),0h80,0hBF) 
ENDFUNC


4 天前 22:00
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:10899
专家分:43509
注 册:2014-5-20
收藏
得分:0 
以下是引用吹水佬在2025-11-19 22:00:46的发言:

用下面的 IsUTF8Chinese() 检测 0x4E00 ~ 0x9FFF 的UTF-8直接判断好像未见异常。

** 如果字符表达式的前3个字节是一个UTF-8汉字字符,则返回真(.T.)
FUNCTION IsUTF8Chinese(cStr)
    LOCAL bytes
    bytes = LEFT(cStr,3)
    RETURN LEN(bytes)==3 AND;
        BETWEEN(SUBSTR(bytes,1,1),0hE4,0hE9) AND;
        BETWEEN(SUBSTR(bytes,2,1),0h80,0hBF) AND;
        BETWEEN(SUBSTR(bytes,3,1),0h80,0hBF)
ENDFUNC


测试了一下,这个 IsUTF8Chinese() 顶多也只能检测3字节字符是否为UTF-8字符,而不能确定是不是汉字字符。

4 天前 22:48
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:10899
专家分:43509
注 册:2014-5-20
收藏
得分:0 
讲到底UTF-8是Unicode码点的编码之一,只是增加了字节数等标识,不能直接按 UTF-8 字节值来判断。
严谨的判断过程应该是:
1、判断字节序列是否为有效的 UTF-8
2、将 UTF-8 解码为 Unicode 码点
3、判断 Unicode 码点是否落在[0x4E00, 0x9FFF]区间内。

3 天前 00:05
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:10899
专家分:43509
注 册:2014-5-20
收藏
得分:0 
以下是引用cssnet在2025-11-19 17:44:02的发言:

因 UTF-8 字符长度不固定,必须从头至尾扫描掩码,才能确定字符个数。倘若:
Unicode = STRCONV(Utf8, 12)
则:
lenU(Unicode)
SubstrU(Unicode, 3, 5)
leftU(Unicode, 13)
rightU(Unicode, 11)

那瞬间便不再是问题,因 Unicode BMP 平面定长2字节。

UTF-8 字符长度通常不会自己去计算,一般编程环境都有相关库函数。
UTF-8是对Unicode码点的扩展,能屈能伸。


3 天前 00:31
cssnet
Rank: 5Rank: 5
等 级:职业侠客
威 望:5
帖 子:552
专家分:380
注 册:2013-10-4
收藏
得分:0 
以下是引用吹水佬在2025-11-20 00:31:21的发言:

UTF-8 字符长度通常不会自己去计算,一般编程环境都有相关库函数。
UTF-8是对Unicode码点的扩展,能屈能伸。


VFP似乎没有UTF-8相关的字符处理函数。
我们先来看看 LenU() 的VFP实现。
这相当于LenC() 的UTF-8版,返回包含中英数任意组合字符串的字符数目,一个英数计1,一个汉字也计1。
懒得自己写,让DeepSeek直接将C语言版翻译成VFP版:

程序代码:
* 计算UTF[color=#808080]-8字符串的字符数目[/color]
FUNCTION lenU(tcString)
LOCAL lnLen, lnCount, i, lnByte, lnCharLen

IF EMPTY(tcString)
    RETURN 0
ENDIF

lnLen = LEN(tcString)
lnCount = 0
i = 1

DO WHILE i <= lnLen
    lnByte = ASC(SUBSTR(tcString, i, 1))
    lnCharLen = Utf8_char_length(lnByte)
    
    * 确保不越界
    IF i + lnCharLen - 1 > lnLen
        EXIT
    ENDIF
    
    lnCount = lnCount + 1
    i = i + lnCharLen
ENDDO

RETURN lnCount
ENDFUNC

* UTF[color=#808080]-8字符字节长度查找表[/color]
FUNCTION Utf8_char_length(tnByte)
LOCAL aUtf8Table[256], i

* 初始化查找表
DIMENSION aUtf8Table[256]

* 1字节字符: 0xxxxxxx ([color=#800000]0-127)[/color]
FOR i = 1 TO 128
    aUtf8Table[i] = 1
ENDFOR

* 2字节字符: 110xxxxx ([color=#800000]194-223)[/color]
FOR i = 194 TO 223
    aUtf8Table[i] = 2
ENDFOR

* 3字节字符: 1110xxxx ([color=#800000]224-239)  [/color]
FOR i = 224 TO 239
    aUtf8Table[i] = 3
ENDFOR

* 4字节字符: 11110xxx ([color=#800000]240-244)[/color]
FOR i = 240 TO 244
    aUtf8Table[i] = 4
ENDFOR

* 5字节字符: 111110xx ([color=#800000]245-247)[/color]
FOR i = 245 TO 247
    aUtf8Table[i] = 5
ENDFOR

* 6字节字符: 1111110x ([color=#800000]248-249)[/color]
FOR i = 248 TO 249
    aUtf8Table[i] = 6
ENDFOR

* 无效字节: [color=#800000]250-255[/color]
FOR i = 250 TO 255
    aUtf8Table[i] = 1  && 按单字节处理
ENDFOR

* 特殊处理128[color=#808080]-193区间的无效UTF-8首字节[/color]
FOR i = 128 TO 193
    aUtf8Table[i] = 1  && 按单字节处理
ENDFOR

RETURN aUtf8Table[tnByte + 1]  && VFP数组从1开始,字节值从0开始
ENDFUNC

*------------------------------*
程序代码:
//C语言版如下:
//计算单个Utf-8字符的字节长度的宏
//定义查找表,长度256,表中的数值表示以此为起始字节的utf-8字符长度
static unsigned char Utf8_table[] =
{
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
    4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
};
#define Utf8_char_length(x) Utf8_table[(x)]

//计算str的UTF-8字符数目
int lenU(char *str)
{
    int slen = strlen(str);
    int ret = 0;
    for (char *ptr = str;
        *ptr != 0 && ret < slen;
        len++, ptr += Utf8_char_length((uchar)*ptr));
    return ret;
}


VFP版有些臃肿,其实可以定义一个全局数组或弄一个256行的表,那样不必每次调用都初始化。

程序代码:
* VFP另一个简化版本:直接计算字节长度
FUNCTION lenU_Simple(tcString)
LOCAL lnLen, lnCount, i, lnByte, lnCharLen

IF EMPTY(tcString)
    RETURN 0
ENDIF

lnLen = LEN(tcString)
lnCount = 0
i = 1

DO WHILE i <= lnLen
    lnByte = ASC(SUBSTR(tcString, i, 1))
    
    * 直接计算UTF-8字符长度
    DO CASE
        CASE lnByte < 128      && 0xxxxxxx
            lnCharLen = 1
        CASE lnByte < 224      && 110xxxxx
            lnCharLen = 2
        CASE lnByte < 240      && 1110xxxx
            lnCharLen = 3
        CASE lnByte < 248      && 11110xxx
            lnCharLen = 4
        CASE lnByte < 252      && 111110xx
            lnCharLen = 5
        OTHERWISE              && 1111110x
            lnCharLen = 6
    ENDCASE
    
    IF i + lnCharLen - 1 > lnLen
        EXIT
    ENDIF
    
    lnCount = lnCount + 1
    i = i + lnCharLen
ENDDO

RETURN lnCount
ENDFUNC
3 天前 12:27
cssnet
Rank: 5Rank: 5
等 级:职业侠客
威 望:5
帖 子:552
专家分:380
注 册:2013-10-4
收藏
得分:0 
如果忽略 Unicode 扩展部分,只关注USC-2,那么实现就很简单了:
程序代码:
FUNCTION lenU(tcString)
* 注:输入参数是Utf[color=#808080]-8,此处略去验证Utf-8字节有效性部分的代码[/color]
tuString = strconv(tcString, 12)  &&UTF-8 字符转换为 UNICODE 字符。
return int(len(tuString)/2)
ENDFUNC


3 天前 12:46
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:10899
专家分:43509
注 册:2014-5-20
收藏
得分:0 
回复 56楼 cssnet
MultiByteToWideChar api
程序代码:
DECLARE long MultiByteToWideChar IN kernel32 long,long,string,long,string@,long

? lenU(STRCONV("1字2",9))

FUNCTION lenU(tcString)
    RETURN MultiByteToWideChar(65001, 0, tcString, LEN(tcString), NULL, 0)
ENDFUNC
3 天前 15:31
cssnet
Rank: 5Rank: 5
等 级:职业侠客
威 望:5
帖 子:552
专家分:380
注 册:2013-10-4
收藏
得分:0 
VFP版的 SubstrU(UTF8, m, n) 实现有些繁琐:
程序代码:
* 截取UTF[color=#808080]-8字符串:从第m个字符开始,连续n个字符[/color]
FUNCTION substrU(tcString, tnStart, tnCount)
LOCAL lnTotalChars, lnStartPos, lnEndPos, lnCharsNeeded
LOCAL lnBytePos, lnCharCount, lnByte, lnCharLen, lcResult

IF EMPTY(tcString) OR tnStart < 1
    RETURN ""
ENDIF

* 获取字符串总字符数
lnTotalChars = lenU(tcString)

* 处理起始位置超出范围的情况
IF tnStart > lnTotalChars
    RETURN ""
ENDIF

* 计算结束位置
IF EMPTY(tnCount) OR tnCount < 1
    lnEndPos = lnTotalChars
ELSE
    lnEndPos = MIN(tnStart + tnCount - 1, lnTotalChars)
ENDIF

* 查找起始字节位置
lnStartPos = FindUtf8CharPos(tcString, tnStart)
IF lnStartPos = 0
    RETURN ""
ENDIF

* 查找结束字节位置
lnEndPos = FindUtf8CharPos(tcString, lnEndPos + 1) - 1
IF lnEndPos < lnStartPos
    lnEndPos = LEN(tcString)
ENDIF

* 截取子字符串
RETURN SUBSTR(tcString, lnStartPos, lnEndPos - lnStartPos + 1)
ENDFUNC

* 查找第n个UTF[color=#808080]-8字符的字节位置[/color]
FUNCTION FindUtf8CharPos(tcString, tnCharIndex)
LOCAL lnLen, lnCharCount, i, lnByte, lnCharLen

IF EMPTY(tcString) OR tnCharIndex < 1
    RETURN 1
ENDIF

lnLen = LEN(tcString)
lnCharCount = 0
i = 1

DO WHILE i <= lnLen
    lnCharCount = lnCharCount + 1
    
    IF lnCharCount = tnCharIndex
        RETURN i
    ENDIF
    
    lnByte = ASC(SUBSTR(tcString, i, 1))
    lnCharLen = Utf8_char_length(lnByte)
    
    IF i + lnCharLen - 1 > lnLen
        EXIT
    ENDIF
    
    i = i + lnCharLen
ENDDO

RETURN LEN(tcString) + 1  && 返回字符串末尾+1的位置
ENDFUNC

* UTF[color=#808080]-8字符字节长度查找函数(复用之前的)[/color]
FUNCTION Utf8_char_length(tnByte)
LOCAL lnCharLen

DO CASE
    CASE tnByte < 128      && 0xxxxxxx
        lnCharLen = 1
    CASE tnByte < 224      && 110xxxxx
        lnCharLen = 2
    CASE tnByte < 240      && 1110xxxx
        lnCharLen = 3
    CASE tnByte < 248      && 11110xxx
        lnCharLen = 4
    CASE tnByte < 252      && 111110xx
        lnCharLen = 5
    OTHERWISE              && 1111110x
        lnCharLen = 6
ENDCASE

RETURN lnCharLen
ENDFUNC

* 计算UTF[color=#808080]-8字符串字符数(复用之前的)[/color]
FUNCTION lenU(tcString)
LOCAL lnLen, lnCount, i, lnByte, lnCharLen

IF EMPTY(tcString)
    RETURN 0
ENDIF

lnLen = LEN(tcString)
lnCount = 0
i = 1

DO WHILE i <= lnLen
    lnByte = ASC(SUBSTR(tcString, i, 1))
    lnCharLen = Utf8_char_length(lnByte)
    
    IF i + lnCharLen - 1 > lnLen
        EXIT
    ENDIF
    
    lnCount = lnCount + 1
    i = i + lnCharLen
ENDDO

RETURN lnCount
ENDFUNC
3 天前 16:07
cssnet
Rank: 5Rank: 5
等 级:职业侠客
威 望:5
帖 子:552
专家分:380
注 册:2013-10-4
收藏
得分:0 
原想分别实现LeftU()和RigtU(),结果DeepSeek给出了增强版substrU()的建议,似乎这两个函数也一并解决了。

FUNCTION substrU(Utf8, m, n)

m 可以为负值,意思是:自字符串末尾往前数m个字符。则,
RigtU(Utf8, 5) = substrU(Utf8, -5)

LeftU(Utf8, 3) = substrU(Utf8, 1, 3)

另,n 也可以为负值,不过感觉有些晦涩,忽略。

3 天前 17:37
cssnet
Rank: 5Rank: 5
等 级:职业侠客
威 望:5
帖 子:552
专家分:380
注 册:2013-10-4
收藏
得分:0 
以下是引用吹水佬在2025-11-20 15:31:36的发言:

MultiByteToWideChar api

DECLARE long MultiByteToWideChar IN kernel32 long,long,string,long,string@,long

? lenU(STRCONV("1字2",9))

FUNCTION lenU(tcString)
    RETURN MultiByteToWideChar(65001, 0, tcString, LEN(tcString), NULL, 0)
ENDFUNC


MultiByteToWideChar 支持 Unicode 扩展平面,确实是最高效的科学解决方案!之前一门心思数字节的方案太LOW啦!
3 天前 17:49
快速回复:判断一个字符是否纯汉字(非标点符号、英数等)的函数
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.031013 second(s), 11 queries.
Copyright©2004-2025, BC-CN.NET, All Rights Reserved