lua实现查找一个字符串中的某些字符

本文深入解析Lua编程语言中用于查找子字符串的内置函数string.find的使用方法,通过实例演示如何准确获取目标子串的起始和结束位置,并详细解释查找失败时的返回值及错误处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

local starts, ends = string.find(string, char)

在lua中string的下标从1开始

例:

starts, ends = string.find("ahsss3", "hs")

print(starts, ends)

打印结果是  2, 3

上例如果是查找“sbc”  返回是nil,直接print会报错

<think>我们使用Lua进行字符串处理。由于中文字符在UTF-8编码下通常占用3个字节(个别生僻字可能4个字节),但我们可以通过检查字符的编码范围来判断。在UTF-8编码中,中文字符的编码范围一般是:第一个字节:0xE4到0xE9(对应十进制228到233)或者0x80到0xBF(但通常我们看第一个字节的范围)更准确地说,中文字符的Unicode范围主要在4E00-9FFF之间,转换成UTF-8编码后,第一个字节的范围是0xE4(228)到0xE9(233),后面两个字节在0x80(128)到0xBF(191)之间。但是,在Rime的Lua环境中,我们通常处理的是UTF-8编码的字符串。我们可以遍历字符串中的每个字符(UTF-8字符),然后判断该字符的Unicode码是否在中文的范围内(如:0x4E00到0x9FA5,这是常用的中文字符范围,但更全面的范围可以扩展到0x9FFF或更大)。注意:Rime的Lua环境可能没有内置的Unicode处理库,所以我们需要自己写一个简单的UTF-8解码器,或者利用Lua字符串模式匹配来遍历每个UTF-8字符。方法:遍历字符串中的每个UTF-8字符,然后判断该字符的编码是否在中文的范围内。步骤:1.使用Lua的`for`循环和模式匹配来遍历字符串中的每个UTF-8字符(使用`[%z\1-\127\194-\244][\128-\191]*`这样的模式可以匹配一个UTF-8字符,但更简单的是使用`utf8.codes`,然而Rime的Lua环境可能没有`utf8`库,所以我们用另一种方法)。2.由于Rime的Lua环境可能没有完整的`utf8`库,我们可以自己写一个遍历函数,利用Lua字符串操作。另一种思路:使用模式匹配来查找是否有中文字符。我们可以使用一个模式来匹配一个中文字符(即编码范围在0x4E00-0x9FFF之间的字符)。在UTF-8编码中,这个范围对应的字节模式为:0xE40x80-0xBF0x80-0xBF到0xE90x80-0xBF0x80-0xBF但是,实际上中文字符的起始范围是0x4E00,对应的UTF-8编码是:111001001011100010000000->0xE40xB80x80结束范围0x9FFF:111010011011111110111111->0xE90xBF0xBF因此,我们可以写一个模式来匹配这个范围内的字符。但是,由于Lua的模式匹配不支持直接指定字节范围(只能指定字符范围,而字节是0-255),我们可以用`[\228-\233][\128-\191][\128-\191]`来匹配,但是注意,并不是这个范围内的所有字符都是中文,因为0xE4到0xE9开头的三字节字符还包括其他语言的字符,但是中文在这个范围内。另外,我们还需要考虑四字节的中文字符?实际上,常用的中文字符都在三字节范围内,四字节的非常少,如果考虑全面,我们可以将四字节的中文字符也包含进来。四字节的中文字符:Unicode扩展区(比如0x20000到0x2A6DF等)是四字节,但数量较少。在Rime输入法中,通常我们只考虑基本多文种平面(BMP)内的中文字符(即0x4E00-0x9FFF)已经足够。因此,我们可以这样判断:如果一个字符串中包含一个三字节的字符,且其编码在0x4E00到0x9FFF之间,那么我们就认为它包含中文。但是,由于我们无法直接用Lua的模式匹配来检查Unicode码点,所以我们可以这样:方案1:遍历字符串,对于每个字节,我们检查连续的三个字节是否符合中文字符的编码范围。但是这种方法需要处理字符串的每个字节,并注意不要越界。方案2:使用Lua的`string.find`函数,并构造一个模式,这个模式能够匹配一个中文字符(即三字节,且第一个字节为0xE4到0xE9,后面两个字节在0x80-0xBF之间)。但是,这个模式会匹配所有三字节的UTF-8字符(包括中文、日文、韩文等),但如果我们只关心中文,那么可以认为只要出现了这样的三字节字符,就可能是中文(因为中文字符数量庞大,出现这样的字符很大概率是中文)。不过,为了更准确,我们可以进一步缩小范围,即第一个字节为0xE4到0xE9,第二个字节和第三个字节在特定范围内,但这样并不完全准确,因为中文字符的编码在这三个字节中分布很广。实际上,我们也可以直接匹配Unicode范围,但需要将UTF-8字节序列转换成Unicode码点。我们可以写一个函数,将三个字节转换为一个码点。由于Rime的Lua环境没有内置这样的转换函数,我们可以自己实现一个简单的转换:对于三字节UTF-8编码:第一个字节高4位是1110,接下来4位是码点的高4位;第二个字节高2位是10,接下来6位是码点的中间6位;第三个字节高2位是10,接下来6位是码点的低6位。所以,码点=(第一个字节低4位)<<12|(第二个字节低6位)<<6|(第三个字节低6位)然后判断这个码点是否在0x4E00到0x9FFF之间。但是,这种方法需要遍历整个字符串,并且每次处理一个字符(可能1-4字节),然后逐个判断。考虑到性能,如果字符串很长,可能会影响输入法的响应速度。不过,通常输入法处理的字符串不会很长。我们选择方案2,但为了准确性,我们只判断是否包含中文字符,而不需要知道具体位置,所以一旦发现一个中文字符就可以返回。实现步骤:1.编写一个函数`contains_chinese(str)`,遍历字符串中的每个UTF-8字符。2.如何遍历:我们用一个索引`i`从1到字符串长度,然后根据当前字节判断这个字符占几个字节。-如果当前字节(设为`byte`)的值在0-127,则为一个字节(ASCII字符),跳过。-如果当前字节在194-223(0xC2-0xDF)之间,则占两个字节,跳过两个字节。-如果当前字节在224-239(0xE0-0xEF)之间,则占三个字节,此时我们取出这三个字节,计算码点,判断是否在0x4E00-0x9FFF之间,如果是,返回true。-如果当前字节在240-244(0xF0-0xF4)之间,则占四个字节,跳过四个字节(或者也可以计算码点,判断是否在扩展中文区域,但这里我们只考虑基本区,所以四字节的我们暂时忽略?或者也可以检查扩展区的中文,但扩展区范围较大,且使用较少,我们可以暂时忽略,或者只判断基本区就够了)。3.注意:跳过字符时,我们需要检查后面是否还有足够的字节。但是,为了简化,我们只考虑三字节的中文基本区。因为四字节的中文非常少见,而且输入法输入时通常不会输入(除非是生僻字),所以忽略它们对实际应用影响不大。另外,我们也可以利用Rime自带的字符类型判断?但是Rime的Lua环境并没有提供这样的函数。因此,我们决定写一个遍历函数,只检查三字节的字符,并且码点在0x4E00到0x9FFF之间。具体代码实现:```luafunctioncontains_chinese(str)locali=1locallen=string.len(str)whilei<=lendolocalc=string.byte(str,i)--单字节字符ifc<0x80theni=i+1--两字节字符elseifc>=0xC0andc<=0xDFtheni=i+2--三字节字符elseifc>=0xE0andc<=0xEFthen--检查后面两个字节是否存在ifi+2>lenthenbreakendlocalb1=string.byte(str,i+1)localb2=string.byte(str,i+2)--检查b1和b2是否在0x80-0xBF之间ifb1andb2andb1>=0x80andb1<=0xBFandb2>=0x80andb2<=0xBFthen--计算码点localcode_point=(c%0x10)*0x1000+(b1%0x40)*0x40+(b2%0x40)--判断是否在中文基本区ifcode_point>=0x4E00andcode_point<=0x9FFFthenreturntrueendendi=i+3--四字节字符elseifc>=0xF0andc<=0xF7theni=i+4else--其他情况(如无效字节),跳过一字节i=i+1endendreturnfalseend```注意:上面的计算码点的方式是:c的低4位(因为高4位是1110)作为高4位,乘以2^12(即0x1000);b1的低6位作为中间6位,乘以2^6(即0x40);b2的低6位作为低6位。所以:code_point=(c%16)*4096+(b1%64)*64+(b2%64)但是,这样计算对吗?我们以字符'一'为例,它的Unicode码点是0x4E00,UTF-8编码是0xE40xB80x80。c=0xE4->228,228%16=4->4*4096=16384b1=0xB8->184,184%64=56->56*64=3584b2=0x80->128,128%64=0所以code_point=16384+3584+0=19968->0x4E00,正确。因此,这个计算方式是正确的。但是,上面的代码只检查了三字节字符中在中文基本区的字符。如果字符串中包含四字节的中文字符,则不会被检测到。如果需要检测四字节的中文字符,可以类似地处理四字节字符,并检查其码点是否在扩展区(比如0x20000到0x2A6DF等),但这里我们省略。另外,上面的代码只检查了后面两个字节是否在0x80-0xBF之间,如果不在,则跳过这个字符(因为可能是无效的UTF-8序列),我们不做处理,直接跳过。使用这个函数,我们就可以判断字符串中是否包含中文了。但是,在Rime输入法中,我们可能更希望用简单的方法:直接匹配三字节的字符,并且第一个字节在0xE4到0xE9之间(因为0xE4到0xE9覆盖了4E00到9FFF的UTF-8编码的第一个字节)。这样我们可以不用计算码点,但准确性稍差(因为0xE0到0xE3开头的三字节字符不在中文基本区,而0xEA到0xEF开头的三字节字符可能是其他语言的字符,也可能包括一些扩展汉字,但扩展汉字很少)。因此,我们可以简化:只检查第一个字节在0xE4到0xE9之间的三字节字符,就认为是中文字符。这样虽然会漏掉0xE4之前的和0xE9之后的,但是中文基本区大部分都在这个范围内。不过,0x4E00到0x4E00+0x1000(即0x5E00)之间的字符用0xE4表示,0x5E00到0x6E00之间的字符用0xE5表示,依此类推,直到0x9E00到0x9FFF用0xE9表示。所以,0xE4到0xE9覆盖了0x4E00到0x9FFF的大部分,但严格来说,0x9FFF是0xE9开头的最后一个字符,所以覆盖完整。所以,我们可以这样写:```luafunctioncontains_chinese(str)locali=1locallen=string.len(str)whilei<=lendolocalc=string.byte(str,i)ifc>=0xE4andc<=0xE9then--检查后面两个字节是否存在ifi+2<=lenthenlocalb1=string.byte(str,i+1)localb2=string.byte(str,i+2)--检查b1和b2是否在0x80-0xBF之间ifb1andb2andb1>=0x80andb1<=0xBFandb2>=0x80andb2<=0xBFthenreturntrueendendi=i+3elseifc<0x80theni=i+1elseifc>=0xC0andc<=0xDFtheni=i+2elseifc>=0xE0andc<=0xEFtheni=i+3elseifc>=0xF0andc<=0xF7theni=i+4elsei=i+1endendreturnfalseend```这个版本更简单,而且性能更好,因为一旦遇到0xE4到0xE9之间的字节,并且后面两个字节是有效的,就返回true。但需要注意的是,0xE4到0xE9之间的字节开头的三字节字符并不全是中文,比如藏文、蒙古文等也在三字节范围内,但是它们的第一个字节可能不在0xE4到0xE9之间(比如藏文是0xF0开头,四字节)。所以,这个简化版本在大多数情况下是准确的(因为中文输入法处理的文本中,出现0xE4到0xE9开头的三字节字符,基本就是中文)。因此,我们可以使用这个简化版本。但是,为了确保不遗漏,我们也可以将范围扩大到0xE0到0xEF,然后检查码点是否在中文范围内。不过,我们之前已经写了一个计算码点的版本,所以我们可以结合一下:先通过第一个字节范围缩小到三字节字符,然后计算码点,再判断是否在中文基本区。这样既不会误判,也不会漏判。考虑到性能,我们可以先判断第一个字节是否在0xE0到0xEF之间(三字节),然后计算码点,再判断是否在0x4E00到0x9FFF之间。这样代码既准确又不会太慢。修改后的代码:```luafunctioncontains_chinese(str)locali=1locallen=string.len(str)whilei<=lendolocalc=string.byte(str,i)--三字节字符ifc>=0xE0andc<=0xEFthenifi+2<=lenthenlocalb1=string.byte(str,i+1)localb2=string.byte(str,i+2)ifb1andb2andb1>=0x80andb1<=0xBFandb2>=0x80andb2<=0xBFthen--计算码点localcode_point=(c%0x10)*0x1000+(b1%0x40)*0x40+(b2%0x40)ifcode_point>=0x4E00andcode_point<=0x9FFFthenreturntrueendendendi=i+3elseifc<0x80theni=i+1elseifc>=0xC0andc<=0xDFtheni=i+2elseifc>=0xF0andc<=0xF7theni=i+4elsei=i+1endendreturnfalseend```这个版本检查所有三字节字符,但只对在中文基本区内的字符返回true。现在,我们可以在Rime的Lua脚本中使用这个函数了。示例使用:ifcontains_chinese("hello")then...end--返回falseifcontains_chinese("你好")then...end--返回true注意:这个函数只能检测中文基本区,如果字符串中包含扩展区的中文字符,则不会被检测到。如果需要检测扩展区,可以类似地处理四字节字符,并判断码点是否在扩展区范围内。考虑到扩展区的中文字符在输入法中较少使用,我们可以暂时忽略。因此,我们提供上述函数作为解决方案。最后,由于Rime的Lua环境可能对性能要求较高,如果字符串很长,这个函数可能会稍微耗时。但在输入法场景中,字符串通常是用户输入的内容,长度有限,所以可以接受。另外,如果Rime的Lua环境支持`utf8`库(比如在较新的Lua版本中),我们可以使用更简单的方法:```lua--如果支持utf8库functioncontains_chinese(str)for_,cinutf8.codes(str)doifc>=0x4E00andc<=0x9FFFthenreturntrueendendreturnfalseend```但是,Rime的Lua环境通常不支持`utf8`库(因为Rime使用的是Lua5.1,且没有包含utf8库)。所以,我们使用前面的方法。综上所述,我们选择遍历并计算码点的方法。代码已给出,并附有详细注释。</think>在 Rime 的 Lua 脚本中判断字符串是否包含中文,可以通过检查字符的 Unicode 范围来实现。中文汉字主要分布在 Unicode 的 **CJK Unified Ideographs** 区域(U+4E00 到 U+9FFF)。以下是完整的解决方案: ### Lua 实现代码 ```lua function contains_chinese(str) for uchar in string.gmatch(str, "[%z\1-\127\194-\244][\128-\191]*") do local byte = string.byte(uchar) -- 检查三字节字符 (中文通常为三字节UTF-8) if #uchar == 3 and byte >= 0xE4 and byte <= 0xE9 then local code = (byte % 16) * 4096 + (string.byte(uchar, 2) % 64) * 64 + (string.byte(uchar, 3) % 64) if code >= 0x4E00 and code <= 0x9FFF then return true end end end return false end -- 测试用例 print(contains_chinese("Hello World")) -- false print(contains_chinese("你好,世界!")) -- true print(contains_chinese("こんにちは")) -- false (日文) print(contains_chinese("안녕하세요")) -- false (韩文) print(contains_chinese("Mixed 中文 English")) -- true ``` ### 代码解释 1. **UTF-8 字符遍历**: ```lua for uchar in string.gmatch(str, "[%z\1-\127\194-\244][\128-\191]*") do ``` - 使用 `string.gmatch` 和 UTF-8 模式匹配遍历字符串中的每个字符。 - 模式说明: - `[%z\1-\127]`:匹配单字节 ASCII 字符 - `[\194-\244]`:匹配多字节字符的首字节 - `[\128-\191]*`:匹配多字节字符的后续字节 2. **中文检测逻辑**: ```lua if #uchar == 3 and byte >= 0xE4 and byte <= 0xE9 then ``` - 中文在 UTF-8 中通常占用 3 字节(基本汉字区)。 - 首字节范围 `0xE4-0xE9` 对应汉字区的 UTF-8 编码。 3. **Unicode 码点计算**: ```lua local code = (byte % 16) * 4096 + (string.byte(uchar, 2) % 64) * 64 + (string.byte(uchar, 3) % 64) ``` - 将 UTF-8 的三字节编码转换为 Unicode 码点。 - 计算公式: `code = (b1 & 0x0F) << 12 | (b2 & 0x3F) << 6 | (b3 & 0x3F)` 4. **中文范围检查**: ```lua if code >= 0x4E00 and code <= 0x9FFF then ``` - Unicode 汉字基本区范围:`U+4E00` 到 `U+9FFF`(共 20,992 个汉字)。 ### 注意事项 - **扩展汉字区**:如需支持扩展汉字(如 `U+20000` 到 `U+2A6DF`),需额外处理四字节 UTF-8 字符。 - **性能优化**:当检测到第一个中文汉字时立即返回 `true`,避免不必要的遍历。 - **编码范围**:此实现严格区分中文与其他 CJK 字符(日文、韩文)。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值