注册 登录
编程论坛 VFP论坛

判断一个字符是否纯汉字(非标点符号、英数等)的函数

cssnet 发布于 2025-11-17 14:52, 2435 次点击
类似这种基础函数,现在习惯交给DeepSeek来写了。
不知有无遗漏?——

FUNCTION IsPureChinese(tcChar)
* 判断输入参数是否为纯汉字?
* 注:只预设两种输入参数编码:GBK或UTF-8。
LOCAL llResult, lcChar

lcChar = ALLTRIM(tcChar)

IF EMPTY(tcChar)
    RETURN .F.
ENDIF

* 自动检测编码并调用相应函数
DO CASE
    CASE LEN(lcChar) = 1    && 单字节,肯定不是汉字
        RETURN .F.
        
    CASE LEN(lcChar) = 2    && 可能是GBK编码
        lcChar = strconv(lcChar, 9)    && 将双字节字符也转换为 UTF-8
        RETURN IsUTF8Chinese(lcChar)
        
    CASE LEN(lcChar) >= 3    && 可能是UTF-8编码
        RETURN IsUTF8Chinese(lcChar)
        
    OTHERWISE
        RETURN .F.
ENDCASE
ENDFUNC

* UTF-8编码汉字判断
FUNCTION IsUTF8Chinese(tcChar)
LOCAL lnByte1, lnByte2, lnByte3, lnLen, lnUnicode

lcUnicode = strconv(tcChar, 12)    && 将 UTF-8 转换为 Unicode
* 小端序:低位在前,高位在后
lnLow = ASC(SUBSTR(tcUnicodeStr, 1, 1))
lnHigh = ASC(SUBSTR(tcUnicodeStr, 2, 1))
lnUnicode = lnHigh * 256 + lnLow
* 汉字Unicode范围:0x4E00-0x9FFF
RETURN (lnUnicode >= 0x4E00 AND lnUnicode <= 0x9FFF)
ENDFUNC



[此贴子已经被作者于2025-11-18 12:09编辑过]

66 回复
#2
吹水佬2025-11-17 15:11
汉字Unicode范围:0x4E00-0x9FFF
这个是 UTF16,不是 UTF-8 ?


#3
吹水佬2025-11-17 15:28
这样看看有冇搞头
cstr  = "汉字"
uinc = STRCONV(cstr,5)
utf8 = STRCONV(cstr,9)
? STRCONV(STRCONV(cstr,6),5) == cstr
? STRCONV(STRCONV(uinc,6),5) == uinc
? STRCONV(STRCONV(utf8,6),5) == utf8

? STRCONV(STRCONV(cstr,11),9) == cstr
? STRCONV(STRCONV(uinc,11),9) == uinc
? STRCONV(STRCONV(utf8,11),9) == utf8
#4
cssnet2025-11-17 15:36
以下是引用吹水佬在2025-11-17 15:11:21的发言:
汉字Unicode范围:0x4E00-0x9FFF
这个是 UTF16,不是 UTF-8 ?


没错!Deepseek在一开头偷了个懒:
lcUnicode = strconv(tcChar, 12)
其实这样是有风险的:事关,超出3字节的UTF-8,在转换成Unicode的过程中会有损。
只不过平时我也不太可能处理到4字节以上的UTF-8,睁一只眼闭一只眼,当睇唔见啦。
#5
csyx2025-11-17 16:35
以下是引用cssnet在2025-11-17 14:52:48的发言:
* GBK汉字编码范围:
* 首字节:0xB0-0xF7
* 尾字节:0xA1-0xFE

不知你这是从哪位误人子弟的学者处抄来的
瞧瞧不用大字体都看不出差别的这哥俩,符合这扯蛋的编码范围不
? '丢',IsLeadByte('丢'), trans('丢', '@0')
? '丟',IsLeadByte('丟'), trans('丟', '@0')

大概率是把 GBK 和 GB2312 搞混了,但 GB2312 的首字节范围(0xA1-0xF7)也比它大

[此贴子已经被作者于2025-11-17 16:43编辑过]

#6
kangss2025-11-17 17:22
全角的标点符合、数字、字母,算不算汉字?

https://baike.baidu.com/item/全角/9323113
全角:是指中GB2312-80(《信息交换用汉字编码字符集·基本集》)中的各种符号,如A、B、C、1、2、3等,应将这些符号理解为汉字

“判断一个字符是否纯汉字(非标点符号、英数等)的函数”的意图是什么呢?
#7
yiyanxiyin2025-11-17 17:46
提问没有, 你应该这样问ds: 一段编码明确的文字(要么是utf8,要么是gbk), 怎么判断这段文字都是汉字

如果编码不明确, 那么这个问题会上升到一个很高的高度, 需要自动判断文字的编码, 文字少很难, 如果是大量的文字判断准确度会高一些

还有gbk就别考虑了, 都是淘汰的编码, 除非你的系统是个古董, 所以一发现ds给出的代码有判断编码, 那么你就应该立即补充你的问题:只考虑utf8
#8
cssnet2025-11-17 21:27
以下是引用csyx在2025-11-17 16:35:56的发言:

不知你这是从哪位误人子弟的学者处抄来的
瞧瞧不用大字体都看不出差别的这哥俩,符合这扯蛋的编码范围不
? '丢',IsLeadByte('丢'), trans('丢', '@0')
? '丟',IsLeadByte('丟'), trans('丟', '@0')

大概率是把 GBK 和 GB2312 搞混了,但 GB2312 的首字节范围(0xA1-0xF7)也比它大


感谢指正!确实是我的疏忽!
代码是 DeepSeek 给的,我只看了 UTF-8(Unicode)部分,没留意到 GBK 有Bug。
当然,这函数只能针对常用汉字(即 Unicode 编码 0x4E00 ~ 0x9FA5 的20902个汉字),至于后来 Unicode 不断扩展的罕用字,搞得比较复杂,无兴致逐一去枚举。
其实 GBK 编码的Bug,解决方案也简单:

    CASE LEN(lcChar) = 2    && 可能是GBK编码
        lcChar = strconv(lcChar, 9)    && 将双字节字符也转换为 UTF-8
        RETURN IsUTF8Chinese(lcChar)
   
统一用 UTF-8(准确地说,最终是转换为Unicode)去判断即可。

补充一点:未提供容错处理,无法通用。比如说:若输入的字符参数本身就是2字节的 Unicode LE/BE,这么strconv()转来转去,全乱套了!从这一点也可以看出 UTF-8 相比 UTF-16 的优势了:
UTF-16有大尾和小尾(BE/LE)的区别;你随便输入一个2字节的字符参数,根本无法精准地确认:它究竟是ANSI?或者是 UTF-16 LE?或者是UTF-16 BE?然而,你随便输入一个 UTF-8 ,傻瓜都能立刻确认它是且仅是 UTF-8 !
#9
cssnet2025-11-17 21:47
以下是引用kangss在2025-11-17 17:22:15的发言:

“判断一个字符是否纯汉字(非标点符号、英数等)的函数”的意图是什么呢?


原本我跟 DeepSeek 讨论的是另一个问题:
输入一个汉字字符串,如何判断它是一句话,或是一段话(有两句或以上)?
考虑以下例外的情形:

我的天哪!!!
我的天哪!!!……
你难道不惭愧吗?!
你难道不惭愧吗!?

单纯依靠 OCCURS( [句末标志符号(。!?.!?)] ) ,无法准确判断汉字字符串是否有两句话以上;必须确认:在一个[句末标志符号]后边,又出现了至少一个纯汉字,才可断定它有两句或以上。
#10
cssnet2025-11-17 22:20
以下是引用csyx在2025-11-17 16:35:56的发言:
? '丢',IsLeadByte('丢'), trans('丢', '@0')
? '丟',IsLeadByte('丟'), trans('丟', '@0')


遇到一个有趣的问题——

? '丢' = 0hB6AA   && 返回值 .T.
? '丢' == 0hB6AA   && 返回值 .T.
? TRANSFORM(0hB6AA, "@0")   && 返回值 "B6AA"
? TRANSFORM('丢', "@0")   && 返回值 "丢"

这是什么原因?有没有办法,让 TRANSFORM('丢', "@0") 也返回 "B6AA" ?


翻了一下 VFP 帮助文档,原来如此——

? STRCONV('丢', 15)   && 返回值 "B6AA"
? TRANSFORM(STRCONV('丢', 15), "@0")   && 返回值 "B6AA"




[此贴子已经被作者于2025-11-17 22:47编辑过]

#11
吹水佬2025-11-17 22:46
回复 10楼 cssnet
"@0"格式对字符串?
#12
cssnet2025-11-17 22:49
以下是引用吹水佬在2025-11-17 22:46:19的发言:

"@0"格式对字符串?


"@0"格式对字符串,据说会直接返回原字符,不出错。

#13
吹水佬2025-11-17 22:59
以下是引用cssnet在2025-11-17 22:49:16的发言:



"@0"格式对字符串,据说会直接返回原字符,不出错。


试试去掉"@0"
#14
吹水佬2025-11-17 23:06
回复 楼主 cssnet
判断一个字符是否纯汉字(非标点符号、英数等)
先算出字符的ANSI编码,再看看是否落在16~87区(GB2312-80)。

#15
cssnet2025-11-17 23:35
有些迷乱!
试试这个:

ANSI = "丢"
Hex = STRCONV(ANSI, 15)
? Hex   && 返回:B6AA

UTF8 = STRCONV(ANSI, 9)
Hex = STRCONV(UTF8, 15)
? Hex   && 返回:E4B8A2

UNICODE = STRCONV(ANSI, 5)
Hex = STRCONV(UNICODE, 15)
? Hex   && 返回:224E

最后一个 UNICODE 的转换比较晕!因"丢"的Unicode编码是U+4E22,这大尾、小尾搞得人很迷糊;而前边两个倒是符合日常使用习惯的。
#16
csyx2025-11-18 00:06
以下是引用cssnet在2025-11-17 22:20:56的发言:
有没有办法,让 TRANSFORM('丢', "@0") 也返回 "B6AA" ?
这样
? '丢',IsLeadByte('丢'), trans(0h+'丢', '@0')
? '丟',IsLeadByte('丟'), trans(0h+'丟', '@0')
只有本站会员才能查看附件,请 登录
#17
csyx2025-11-18 00:32
这就两个很普通的 GBK 字符,我用搜狗拼音输入的时候它们就挨着,选错二三次就被输入法的自动调频整迷糊了,反正我是没注意过空格上屏的究竟是哪个,直到在源码中搜注释内容没搜到,才知道还有他们哥俩,丢这个口语字两广人应该都熟,我的源码注释里有不少用它“歌颂”vfp.bug 的地方
#18
吹水佬2025-11-18 07:22
'丢' 一级汉字
'丟' 非标码
#19
hsfisher2025-11-18 08:29
这个好
#20
easyppt2025-11-18 08:35
大佬们把玩VFP时应该也是一种乐趣
作为小弟,跟着学习膜拜也是充实


#21
cssnet2025-11-18 09:36
之前我一直把 0hB6AA 当作是有些特殊的字符串,Deepseek提醒说,它是 Varbinary 类型(二进制数据),这引起了我的兴趣。一直以来,都希望能像C语言那样,可随时将字符当作数值那样处理,也就是将 0hB6AA 与 0xB6AA 互换着玩。这下子,Varbinary 类型似乎满足了我的需求!嘿嘿。
#22
吹水佬2025-11-18 11:49
回复 21楼 cssnet
车天車地也不知与“纯汉字”有什么关系?
#23
cssnet2025-11-18 14:24
以下是引用吹水佬在2025-11-18 11:49:54的发言:

车天車地也不知与“纯汉字”有什么关系?


当然有关系!让我们换一种“类C”方式重写一下顶楼的“UTF-8编码汉字判断函数”:

* UTF-8编码汉字判断
FUNCTION IsUTF8Chinese(tcChar)
LOCAL lnByte1, lnByte2, lnByte3, lnLen, lnUnicode

lcUnicodeLE = strconv(tcChar, 12)    && 将 UTF-8 转换为 Unicode
lcUnicodeBE = substr(lcUnicodeLE, 2, 1) + LEFT(lcUnicodeLE, 1)
lcUnicodeBE = STRCONV(lcUnicodeBE, 15)
lnUnicodeBE = EVALUATE("0x" + lcUnicodeBE)
RETURN (lnUnicodeBE >= 0x4E00 AND lnUnicodeBE <= 0x9FFF)
ENDFUNC

因VFP的 Unicode 转换结果是小端序,需高低位对调。

[此贴子已经被作者于2025-11-19 15:15编辑过]

#24
吹水佬2025-11-18 20:25
回复 23楼 cssnet
UTF-16核心编码 0x4E00 ~ 0x9FFF,不考虑UTF-16扩展编码?
#25
吹水佬2025-11-18 20:55
以下是引用cssnet在2025-11-17 23:35:01的发言:

UNICODE = STRCONV(ANSI, 5)
Hex = STRCONV(UNICODE, 15)
? Hex   && 返回:224E

最后一个 UNICODE 的转换比较晕!因"丢"的Unicode编码是U+4E22,这大尾、小尾搞得人很迷糊;而前边两个倒是符合日常使用习惯的。

STRCONV()出来的是机内码 0h224E,U+4E22 是 0x4E22
? STRCONV(0h224E,6)
? STRCONV(BINTOC(0x4E22,"2rs"),6)

#26
cssnet2025-11-18 21:11
以下是引用吹水佬在2025-11-18 20:55:18的发言:
STRCONV()出来的是机内码 0h224E,U+4E22 是 0x4E22
? STRCONV(0h224E,6)
? STRCONV(BINTOC(0x4E22,"2rs"),6)


之所以在意这个大端序、小端序,只因 VFP 的 IDE 无法直接显示 Unicode 字符,软件调试时,只能在调试窗口监视其编码的字面值;而小端序我永远反应不过来,永远没办法第一反应直接将224E与“丢”对应起来,4E22才容易对应上。
这也是我只爱 UTF-8 的原因。
UTF-8 无歧义,无混乱,是全宇宙最最最最伟大的字符编码!
——不接受反驳。

#27
cssnet2025-11-18 21:42
以下是引用吹水佬在2025-11-18 20:25:22的发言:

UTF-16核心编码 0x4E00 ~ 0x9FFF,不考虑UTF-16扩展编码?


不考虑。
只考虑 USC-2 子集。
前边看你建议将 UTF-8 转 ANSI,那个子集其实更小哇!
STRCONV(x, 6) 和 STRCONV(x, 11),其实是不那么安全的,一不留神就可能转换出??。

#28
吹水佬2025-11-18 23:40
回复 27楼 cssnet
那就是只考虑 UTF-16 核心编码 0x4E00 ~ 0x9FFF
这样是不是可以直接用 UTF-8 机内码 0hE4B880~0hE9BFBF 判断就可以?

* UTF-8编码汉字判断
FUNCTION IsUTF8Chinese(tcChar)
    RETURN BETWEEN(tcChar,0hE4B880,0hE9BFBF)    && 假设tcChar是3字节UTF-8字符
ENDFUNC
#29
cssnet2025-11-19 01:20
以下是引用吹水佬在2025-11-18 23:40:19的发言:

那就是只考虑 UTF-16 核心编码 0x4E00 ~ 0x9FFF
这样是不是可以直接用 UTF-8 机内码 0hE4B880~0hE9BFBF 判断就可以?

* UTF-8编码汉字判断
FUNCTION IsUTF8Chinese(tcChar)
    RETURN BETWEEN(tcChar,0hE4B880,0hE9BFBF)    && 假设tcChar是3字节UTF-8字符
ENDFUNC


这个……直觉可能不太安全?
1110xxxx
10xxxxxx
10xxxxxx
这种掩码模式,当中存在大量无效缺码,如此简单粗暴比较,心中不太有底气……
另,很重要的一点:
lcUnicodeLE = strconv(tcChar, 12) 将utf-8 转换至 unicode ,不是脱裤子放P,其主要副作用是,自动验证utf-8字符参数的有效性——若自己写,需逐字节验证掩码,代码挺繁琐的。

[此贴子已经被作者于2025-11-19 07:23编辑过]

#30
吹水佬2025-11-19 09:52
以下是引用cssnet在2025-11-19 01:20:05的发言:



这个……直觉可能不太安全?
1110xxxx
10xxxxxx
10xxxxxx
这种掩码模式,当中存在大量无效缺码,如此简单粗暴比较,心中不太有底气……
另,很重要的一点:
lcUnicodeLE = strconv(tcChar, 12) 将utf-8 转换至 unicode ,不是脱裤子放P,其主要副作用是,自动验证utf-8字符参数的有效性——若自己写,需逐字节验证掩码,代码挺繁琐的。

RETURN (lnUnicodeBE >= 0x4E00 AND lnUnicodeBE <= 0x9FFF)
RETURN BETWEEN(tcChar,0hE4B880,0hE9BFBF)
这两句有本质上的差别吗?



#31
cssnet2025-11-19 10:19
以下是引用吹水佬在2025-11-19 09:52:39的发言:

RETURN (lnUnicodeBE >= 0x4E00 AND lnUnicodeBE <= 0x9FFF)
RETURN BETWEEN(tcChar,0hE4B880,0hE9BFBF)
这两句有本质上的差别吗?


大佬啊,区别不在这一句!区别在:
lcUnicodeLE = strconv(tcChar, 12)
输入的3字节字符(它有可能是正确的 utf-8 ,也有可能是乱码,或别的任意东东……)尝试转换至 unicode ,那么,无效字符、伪UTF-8字符会在此处被自动过滤,转换不出 0x4E00--0x9FFF 之间的纯汉字 Unicode 来。
这也就是我所说的“其主要副作用是,自动验证utf-8字符参数的有效性”含义。
若自己写,在函数最前边可能应逐字节验证一下掩码,确保输入的参数是正确的 UTF-8 编码:

lnByte1 = ASC(SUBSTR(tcChar, 1, 1))
lnByte2 = ASC(SUBSTR(tcChar, 2, 1))
lnByte3 = ASC(SUBSTR(tcChar, 3, 1))

* 验证UTF-8编码有效性
IF (lnByte1 >= 0xE4 AND lnByte1 <= 0xE9) AND ;
   (lnByte2 >= 0x80 AND lnByte2 <= 0xBF) AND ;
   (lnByte3 >= 0x80 AND lnByte3 <= 0xBF)
   ...

#32
吹水佬2025-11-19 10:47
回复 31楼 cssnet
你睇D吾睇D,我说的是有前提的:RETURN BETWEEN(tcChar,0hE4B880,0hE9BFBF)    && 假设tcChar是3字节UTF-8字符
“假设tcChar是3字节UTF-8字符”就是省略了判断UTF-8的汉字前提条件,本质上与strconv(tcChar, 12)一样,只是省略了。
因为重点是说 RETURN BETWEEN(tcChar,0hE4B880,0hE9BFBF) 这句,不用从机内码(0h)到字符码(0x)转来转去。
直接从机内码(0h)判断是否为3字节UTF-8也不麻烦。

#33
cssnet2025-11-19 11:10
以下是引用吹水佬在2025-11-19 10:47:37的发言:

你睇D吾睇D,我说的是有前提的:RETURN BETWEEN(tcChar,0hE4B880,0hE9BFBF)    && 假设tcChar是3字节UTF-8字符
“假设tcChar是3字节UTF-8字符”就是省略了判断UTF-8的汉字前提条件,本质上与strconv(tcChar, 12)一样,只是省略了。
因为重点是说 RETURN BETWEEN(tcChar,0hE4B880,0hE9BFBF) 这句,不用从机内码(0h)到字符码(0x)转来转去。
直接从机内码(0h)判断是否为3字节UTF-8也不麻烦。


回到顶楼:

    CASE LEN(lcChar) >= 3    && 可能是UTF-8编码
        RETURN IsUTF8Chinese(lcChar)

按你的代码简洁性喜好,甚至还可以缩减为:

    CASE LEN(lcChar) >= 3    && 可能是UTF-8编码
        RETURN BETWEEN(lcChar,0hE4B880,0hE9BFBF)  && c1 = 0hE5FFF0,它也return .T.啊!

可是——重点是这个可是——我们只是在数字节,数到3,猜测它有可能是UTF-8编码,接下来,似乎有必要确认一下,验证一下:它“确实是、肯定是”,而非“可能是”;你这是完全寄希望于调用者的人品啊?




[此贴子已经被作者于2025-11-19 11:14编辑过]

#34
吹水佬2025-11-19 12:10
IF (lnByte1 >= 0xE4 AND lnByte1 <= 0xE9) AND ;
   (lnByte2 >= 0x80 AND lnByte2 <= 0xBF) AND ;
   (lnByte3 >= 0x80 AND lnByte3 <= 0xBF)
这个不就是“纯汉字”的3bytes的UTF-8吗?

lcUnicodeLE = strconv(tcChar, 12)    && 将 UTF-8 转换为 Unicode
lcUnicodeBE = RIGHT(lcUnicodeLE, 1) + LEFT(lcUnicodeLE, 1)
这两句是将整个字符串转为Unicode,再取首尾字节。
问题:整个UTF-8字符串中含有不是“汉字”(如常见的NULL或换行等)需要考虑吗?是否有一定的局限性。
#35
cssnet2025-11-19 12:49
哈哈哈哈,这个小问题,讨论到此,有些钻牛角尖啦。打住打住。
VFP在0h与字符之间转换,始终需借助函数STRCONV(C, 15)和EVALUATE();另,0h还有个大、小端序的问题,这可能会是一个陷阱。
若确实不想在0h与0x之间反复切换,或许我们也可以尝试强行直接进行0h比较:

* UTF-8编码汉字判断
FUNCTION IsUTF8Chinese(tcChar)
lcUnicodeLE = strconv(tcChar, 12)    && 将 UTF-8 转换为 Unicode
lcUnicodeBE = substr(lcUnicodeLE, 2, 1) + LEFT(lcUnicodeLE, 1)
l0hUnicodeBE = EVALUATE("0h" + STRCONV(lcUnicodeBE, 15))
RETURN BETWEEN(lcUnicodeBE, 0h4E00, 0h9FFF)
ENDFUNC


[此贴子已经被作者于2025-11-19 12:52编辑过]

#36
吹水佬2025-11-19 14:15
大端小端,如果不是跨平台跨设备的数据处理,一般0h是相对固定的。
如果没必要,0h与0x之间不用切换。
如:
内码形式:0h4E00, 0h9FFF
数值形式:0x004E, 0xFF9F
这不是大端小端问题,是同一数据的不同表达形式,实质是一样的。
如果同一数据有两种表现情况:0h4E00 或 0h004E,就有可能是大端小端问题。



#37
吹水佬2025-11-19 14:24
以下是引用cssnet在2025-11-19 12:49:31的发言:

* UTF-8编码汉字判断
FUNCTION IsUTF8Chinese(tcChar)
lcUnicodeLE = strconv(tcChar, 12)    && 将 UTF-8 转换为 Unicode
lcUnicodeBE = substr(lcUnicodeLE, 2, 1) + LEFT(lcUnicodeLE, 1)
l0hUnicodeBE = EVALUATE("0h" + STRCONV(lcUnicodeBE, 15))
RETURN BETWEEN(lcUnicodeBE, 0h4E00, 0h9FFF)
ENDFUNC

lcUnicodeBE = substr(lcUnicodeLE, 2, 1) + LEFT(lcUnicodeLE, 1)
这一句实质就是只考虑字符串转为Unicode后的前两个字节,为何要转换整个字符串?
vfp的 ISLEADBYTE() 也只是考虑第一个字符的第一个字节。UTF-8汉字应是第一个字符的3个字节。

[此贴子已经被作者于2025-11-19 14:27编辑过]

#38
吹水佬2025-11-19 14:38
以下是引用cssnet在2025-11-19 12:49:31的发言:

VFP在0h与字符之间转换,始终需借助函数STRCONV(C, 15)和EVALUATE();
若确实不想在0h与0x之间反复切换,或许我们也可以尝试强行直接进行0h比较:


试试:
程序代码:

FUNCTION IsUTF8Chinese(tcChar)
    LOCAL bytes
    bytes = LEFT(tcChar,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
#39
吹水佬2025-11-19 14:58
以下是引用吹水佬在2025-11-19 14:38:43的发言:


试试:

FUNCTION IsUTF8Chinese(tcChar)
    LOCAL bytes
    bytes = LEFT(tcChar,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

UTF-16 汉字编码机内码核心范围是 0h004E ~ 0hFF9F,转UTF-8应该是 0hE4B880 ~ 0hE9BFBF
程序代码:

FUNCTION IsUTF8Chinese(tcChar)
    LOCAL bytes
    bytes = LEFT(tcChar,3)
    RETURN LEN(bytes)==3 AND;
        BETWEEN(SUBSTR(bytes,1,1),0hE4,0hE9) AND;
        BETWEEN(SUBSTR(bytes,2,1),0hB8,0hBF) AND;
        BETWEEN(SUBSTR(bytes,3,1),0h80,0hBF)
ENDFUNC


#40
cssnet2025-11-19 15:11
以下是引用吹水佬在2025-11-19 14:58:54的发言:
UTF-16 汉字编码机内码核心范围是 0h004E ~ 0hFF9F...


我老人家算术唔好,可你也不带这样子糊弄我噻!

0x9FFF - 0x4E00 = 0x51FF = 20991
0xFF9F - 0x004E = 0xFF51 = 65361

相差3倍有多咧,大佬!
#41
吹水佬2025-11-19 15:37
以下是引用cssnet在2025-11-19 15:11:32的发言:



我老人家算术唔好,可你也不带这样子糊弄我噻!

0x9FFF - 0x4E00 = 0x51FF = 20991
0xFF9F - 0x004E = 0xFF51 = 65361

相差3倍有多咧,大佬!

什么回事?
0xFF9F - 0x004E 那里来的?
#42
吹水佬2025-11-19 15:46
看来还没搞清 0h与0x的关系
#43
吹水佬2025-11-19 15:49
一眼就明,还用去算:
0x9FFF 与 0xFF9F
0x4E00 与 0x004E

#44
cssnet2025-11-19 16:31
以下是引用吹水佬在2025-11-19 15:49:59的发言:

UTF-16 汉字编码机内码核心范围是 0h004E ~ 0hFF9F

一眼就明,还用去算:
0x9FFF 与 0xFF9F
0x4E00 与 0x004E


好吧,你就直接回答:
0h0100 是否落在 0h004E ~ 0hFF9F 区间?
你只需回答:是 或 否
不需任何分析。
(笔者注:我经常这样命令Deepseek。)

#45
吹水佬2025-11-19 17:03
以下是引用cssnet在2025-11-19 16:31:29的发言:



好吧,你就直接回答:
0h0100 是否落在 0h004E ~ 0hFF9F 区间?
你只需回答:是 或 否
不需任何分析。
(笔者注:我经常这样命令Deepseek。)

就当是字符串比较这样说得吧
不知你车来车去又车到 0h0100 与 0h004E ~ 0hFF9F 的比较目的是什么?
直接探讨完善 IsUTF8Chinese 就好了。
#46
cssnet2025-11-19 17:27
以下是引用吹水佬在2025-11-19 17:03:33的发言:
就当是字符串比较这样说得吧
不知你车来车去又车到 0h0100 与 0h004E ~ 0hFF9F 的比较目的是什么?


我意思是:
Unicode汉字基本平面(BMP)的范围是 ‌U+4E00 至 U+9FFF‌,覆盖了绝大多数常用汉字(约20900个)。
而非你信口胡诌的 0h004E ~ 0hFF9F。
这个范围用“大端序”机内码去表述是准确的,然而,当你试图用 Windows 默认的小端序机内码去描述,则会引发混乱。
换句话说,稳妥的字符区间比较是0x,而非0h。
至于用 0h 能够比较 UTF-8,那纯属运气好!内在原因是 UTF-8 的编码设计,极其科学、合理,规避了可能因字节顺序而引发的混乱,这恐怕也是Win10/11的记事本,默认改用 UTF-8 编码存储的主要原因。
但,UTF-8 用在某些地方,则并不如 USC-2方便,比如本例所涉及的汉字编码区间查询。

#47
cssnet2025-11-19 17:44
UTF-8 用在哪些地方不那么方便,我举几个例子,比如说:

lenU(Utf8)
SubstrU(Utf8, 3, 5)
leftU(Utf8, 13)
rightU(Utf8, 11)

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

那瞬间便不再是问题,因 Unicode BMP 平面定长2字节。
#48
吹水佬2025-11-19 19:16
你重心是放在UTF16,我是想不用转到UTF16而直接在UTF8处理,因入口就是UTF8。
因先说定不考虑扩展编码,汉字的UTF8还有不是3字节编码的吗?
之前提到的按每位字节比较来判断是否UTF8汉字未必正确。
#49
吹水佬2025-11-19 20:14
以下是引用cssnet在2025-11-19 17:27:28的发言:



我意思是:
Unicode汉字基本平面(BMP)的范围是 ‌U+4E00 至 U+9FFF‌,覆盖了绝大多数常用汉字(约20900个)。
而非你信口胡诌的 0h004E ~ 0hFF9F。

谁在信口胡诌?
继续吹水,又要我费口水,车来车去你还是没分清0h与0x的关系。
再解释一次:
对于16位无符号整数0x4E00,其内码为0h004E,0x4E00与0h004E是同一个数据的两种表达形式。
所以 0h004E ~ 0hFF9F 也可以理解为 0x4E00 ~ 0x9FFF  
如:
0x4E00 -- 0h004E
0x4E01 -- 0h014E
...... -- ......
0x4E10 -- 0h104E
...... -- ......
0x9876 -- 0h7698
...... -- ......
0x9EFF -- 0hFF9E
0x9FFF -- 0hFF9F

可能是我最初用 BETWEEN(v, 0h..., 0h...) 误导了你。

#50
吹水佬2025-11-19 20:21
最后做一下UTF16与UTF8的汉字编码对照过程就可以找出有无问题
#51
吹水佬2025-11-19 22:00
用下面的 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


12