当前位置: 首页 > news >正文

平价建网站格翻译国外网站做原创

平价建网站格,翻译国外网站做原创,注册文化传媒公司流程和费用,开发公司交房流程及注意事项摘要 这篇文章围绕 Python 的正则表达式 Match 对象(特别是 endpos、lastindex、lastgroup 以及 group / groups 等方法/属性)做一个从浅入深、贴近日常开发场景的讲解。我们会给出一个真实又常见的使用场景:解析由设备/服务发来的“拼接式”…

在这里插入图片描述

摘要

这篇文章围绕 Python 的正则表达式 Match 对象(特别是 endposlastindexlastgroup 以及 group / groups 等方法/属性)做一个从浅入深、贴近日常开发场景的讲解。我们会给出一个真实又常见的使用场景:解析由设备/服务发来的“拼接式”消息流(每条记录由数字 ID 紧跟字母消息组成,记录之间没有明显分隔符),演示如何用正则抓取、如何利用 Match 对象的属性做窗口限制、判断哪一个分组被匹配、以及如何处理可选分组或交替分组的情况。文章风格偏口语化,代码有详细注释并给出测试样例,最后给出复杂度分析和总结性建议。

描述(现实场景说明)

想象这样一个场景:你在做一个物联网网关或日志解析程序,设备发来的数据被拼接成一条长字符串发送过来(比如网络中间某处丢掉了分隔符)。每条“消息”格式类似 12345HELLO(即一串数字表示设备/消息ID,后面跟一段只含字母的载荷),并且这些消息在一个长字符串里连续出现:

"13579helloworld13579helloworld..."

你需要把这些消息切出来、知道每条消息的起止位置、ID、载荷,并且有时候你只想在字符串的一段区间里搜索(比如只处理前 200 字节、或只在 0~100 的窗口里查找)——这时 Match 对象的 endposposlastindexlastgroup 就非常有用了。

此外,复杂的正则经常包含可选分组和交替分支,遇到匹配失败或匹配到不同分支时,我们要快速判断“到底哪一个分支被命中”,lastindex / lastgroup 可以告诉我们最后被匹配到的分组编号和命名分组名——这对调试复杂模式或根据在哪个分组命中来做不同处理非常有帮助。

下面给出一个完整的题解实现(可直接拿去改造到你的项目里)。

题解答案(功能实现概述)

实现一个函数 parse_concatenated_records(text, start=0, end=None),它会:

  1. text[start:end] 的范围内,用正则 (?P<id>\d+)(?:-(?P<payload>[A-Za-z]+))?(或更严格的 (?P<id>\d+)(?P<payload>[A-Za-z]+))查找“数字+字母”形式的记录;
  2. 对每个匹配返回一个字典,包含 id(字符串)、payload(字符串或 None)、匹配的 span(起止位置)、以及该 Match 对象常用的属性:lastindexlastgroupendpos(便于调试或日志记录);
  3. 支持窗口搜索(传入 end 参数限制 endpos,以便只在片段内匹配);
  4. 在示例部分还演示交替分支的情况以说明 lastindex/lastgroup 的实际意义。

下面给出完整代码(含注释),随后逐行解析。

题解代码(Python)

import re
from typing import List, Dict, Optionaldef parse_concatenated_records(text: str, start: int = 0, end: Optional[int] = None) -> List[Dict]:"""从 text[start:end] 中解析出连续的记录,记录格式为:数字 ID 后面接可选的连字符 - 和 字母 payload例如: "12345-HELLO" 或 "67890WORLD"(第二种不含连字符时 payload 直接接在数字后面)返回值:每条记录是一个字典,包含:- id: 字符串形式的数字 ID- payload: 字母负载(字符串),如果没有则为 None- span: (start_pos, end_pos) 在原始 text 中的切片位置- lastindex: Match.lastindex (最后匹配到的组的编号或者 None)- lastgroup: Match.lastgroup (最后匹配到的命名组名或者 None)- endpos: Match.endpos (本次搜索时使用的 end 参数)"""# 编译一个含命名分组的模式:# (?P<id>\d+)          捕获一个或多个数字到命名组 id# (?:-(?P<payload>[A-Za-z]+))?  可选的 '-' + 字母串,捕获到命名组 payload(如果存在)pattern = re.compile(r"(?P<id>\d+)(?:-(?P<payload>[A-Za-z]+))?")results = []pos = start# 如果未指定 end,我们默认使用整个字符串长度search_end = end if end is not None else len(text)# 循环查找,从上次匹配的 end 位置继续,直到找不到while pos < search_end:m = pattern.search(text, pos, search_end)if not m:break# 组字典(注意 payload 可能为 None)gd = m.groupdict()results.append({"id": gd.get("id"),"payload": gd.get("payload"),  # 可能是 None"span": m.span(),"lastindex": m.lastindex,"lastgroup": m.lastgroup,"endpos": m.endpos,})# 向前移动 pos,避免无限循环(如果匹配到了空串要小心)new_pos = m.end()if new_pos == pos:# 防御:如果没有前进(理论上不会发生在我们这个模式下),向前移动 1pos += 1else:pos = new_posreturn results# 另外给一个小工具展示 lastindex / lastgroup 在交替分支时的行为
def demo_alternation(text: str):"""模式包含两个命名分组在交替分支中:(?P<num>\d+)|(?P<tag>[A-Za-z]+)匹配到数字时 lastgroup='num',匹配到字母时 lastgroup='tag'。"""pat = re.compile(r"(?P<num>\d+)|(?P<tag>[A-Za-z]+)")matches = []for m in pat.finditer(text):matches.append({"match": m.group(0),"groups": m.groups(),"lastindex": m.lastindex,"lastgroup": m.lastgroup,"span": m.span(),})return matches

题解代码分析(逐行/模块详细解释)

下面把关键部分逐块分解,讲清楚为什么要这么写、常见坑有哪些:

  1. pattern = re.compile(r"(?P<id>\d+)(?:-(?P<payload>[A-Za-z]+))?")

    • 我们用命名分组 ?P<name>,这样在取值时更语义化(m.groupdict() 会直接给出 {'id': '123', 'payload': 'HELLO'})。
    • (?: ... )? 是非捕获组 + 可选,它包裹 -(?P<payload>[A-Za-z]+),表示 payload 以及前面的连字符可能出现也可能不出现。
    • 这样的模式兼容 12345-HELLO12345HELLO(如果你只想匹配带 - 的形式,把 ? 去掉即可)。
  2. 搜索循环 while pos < search_end: m = pattern.search(text, pos, search_end)

    • 我们使用 search(而不是 findall),因为 search 返回 Match 对象,包含属性 lastindexlastgroupendpos 等,方便教学/调试。
    • pattern.search(text, pos, search_end) 里的 search_end 就是 Match.endpos 的来源:m.endpos 会等于你传入的那个 search_end,这对想要在字符串某个“窗口”里查找非常有用,比如你只想处理前 200 字节。
  3. 结果收集中的 m.lastindexm.lastgroupm.endpos

    • m.lastindex:返回最后一个被匹配的捕获组的编号(从 1 开始)。如果没有任何捕获组被匹配,返回 None。示例:在 (?P<id>\d+)(?:-(?P<payload>[A-Za-z]+))? 中,如果字符串是 12345(没有 payload),则 lastindex == 1(即只匹配了第一组 id);如果是 12345-HELLO,则 lastindex == 2(两组都匹配了)。
    • m.lastgroup:如果最后匹配的组有命名(我们用了 ?P<...>),则返回该命名组的名字(比如 'payload');如果最后匹配的组没有命名或没有被捕获到,则为 None
    • m.endpos:就是 search 时传入的 end 参数(或默认的 len(text))。用它可以知道当前 Match 对象是在什么样的“窗口”参数下产生的;对分区解析或流处理场景很有用。
  4. pos = m.end() 的移动策略

    • 为了避免重复匹配同一段文本,我们在每次匹配后将 pos 移动到 m.end()。如果出现了可匹配空串的模式(我们当前的模式不会),还需额外防御以免无限循环。
  5. demo_alternation 的作用

    • 通过交替分支 (?P<num>\d+)|(?P<tag>[A-Za-z]+),展示 lastindex / lastgroup 的变化:匹配到数字时 lastgroup == 'num',匹配到字母时 lastgroup == 'tag'。在实际中你可能根据哪一支被命中来决定不同的解析逻辑。

示例测试及结果

下面用几个实际字符串举例,看输出结果会是啥(我把预期输出写清楚,方便你 copy 到交互式环境跑):

  1. 基本示例:两个完整记录相连(没有连字符)
s = "13579helloworld13579helloworld"
res = parse_concatenated_records(s)
for r in res:print(r)

预期输出(示意)

{'id': '13579', 'payload': 'helloworld', 'span': (0, 15), 'lastindex': 2, 'lastgroup': 'payload', 'endpos': 30}
{'id': '13579', 'payload': 'helloworld', 'span': (15, 30), 'lastindex': 2, 'lastgroup': 'payload', 'endpos': 30}

解释:

  • 第一个匹配从 015(假设 ‘13579’ 长度 5,‘helloworld’ 长度 10),第二个紧随其后。
  • endpos 因为我们没有传入 end,默认是整个字符串长度 30
  1. 限定搜索窗口(只处理前 15 个字符)
s = "13579helloworld13579helloworld"
res = parse_concatenated_records(s, start=0, end=15)  # 只在前 15 个字符内查找
for r in res:print(r)

预期输出

{'id': '13579', 'payload': 'helloworld', 'span': (0, 15), 'lastindex': 2, 'lastgroup': 'payload', 'endpos': 15}

解释:

  • 因为 end=15,所以第二条记录超出窗口,不会被匹配到。
  • m.endpos 会反映为 15,说明这是一次窗口内的搜索。
  1. 含连字符的示例(payload 是可选的)
s = "123-ABC456DEF789"
res = parse_concatenated_records(s)
for r in res:print(r)

预期输出(示意):

{'id': '123', 'payload': 'ABC', 'span': (0, 7), 'lastindex': 2, 'lastgroup': 'payload', 'endpos': 15}
{'id': '456', 'payload': 'DEF', 'span': (7, 13), 'lastindex': 2, 'lastgroup': 'payload', 'endpos': 15}
{'id': '789', 'payload': None, 'span': (13, 16), 'lastindex': 1, 'lastgroup': 'id', 'endpos': 15}

解释:

  • 最后一条只有数字 789,没有 payload,所以 payloadNonelastindex == 1lastgroup == 'id'
  1. 交替分支示例展示 lastgroup(使用 demo_alternation
s = "abc123XYZ45"
matches = demo_alternation(s)
for m in matches:print(m)

示例输出(示意):

{'match': 'abc', 'groups': (None, 'abc'), 'lastindex': 2, 'lastgroup': 'tag', 'span': (0, 3)}
{'match': '123', 'groups': ('123', None), 'lastindex': 1, 'lastgroup': 'num', 'span': (3, 6)}
{'match': 'XYZ', 'groups': (None, 'XYZ'), 'lastindex': 2, 'lastgroup': 'tag', 'span': (6, 9)}
{'match': '45', 'groups': ('45', None), 'lastindex': 1, 'lastgroup': 'num', 'span': (9, 11)}

解释:

  • 这里 groups() 的返回是 (num, tag) 的顺序(以分组定义顺序为准)。如果某个分支没被匹配到,对应元素为 None
  • lastgroup 告诉你本次匹配到底是哪个命名分组(也就是哪个分支)命中了。

时间复杂度

  • 单次搜索 pattern.search(text, pos, end) 在最坏情况下通常是 O(k)(k = 待扫描的字符数直到找到匹配或到达 end),对于整个循环(我们每次把 pos 前移到 m.end()),整体上对长度为 n = end-start 的字符串,复杂度通常接近 O(n)
  • 注意:如果 pattern 包含回溯较多的子模式(例如大量嵌套的 .*、回溯点很多),正则可能退化为更高复杂度,最坏情况下可能是指数级。但对我们这里的简单模式 \d+[A-Za-z]+ 之类,表现是线性的。

空间复杂度

  • 函数本身额外占用空间主要来自 results 列表(输出),占用 O(m)(m = 匹配到的记录数)。每条记录的大小与捕获到的文本长度有关,但总体可认为是 O(m)(若忽略单条字符串长度的话)。
  • 正则引擎本身有固定的栈/状态开销,但对于简单的逐步匹配,这个是常数级别的。

总结(实用建议与常见坑)

  1. 什么时候看 lastindex / lastgroup

    • 当你的正则包含多个捕获组、可选组或交替分支时,lastindex/lastgroup 能快速告诉你“最后到底哪个组/分支生效了”,这对后续逻辑分流很有用(比如:如果命中了 payload 分组就解析为文本指令,否则只处理 ID)。
  2. endpos 很有用

    • endpos 反映了调用 search 时传入的 end 参数,适合做“窗口式”解析或增量流解析(例如分段读取文件或网络缓冲区时只在当前已读到的位置内匹配)。
  3. 避免空串匹配导致的死循环

    • 每次循环后都要把 pos 前移,如果遇到 m.end() == pos 的情况务必手动 pos += 1,否则会无限循环。
  4. 对复杂模式谨慎使用 findall

    • findall 返回简单的元组/字符串,不会给你 Match 对象,所以拿不到 lastindex / lastgroup / endpos 等调试信息。需要这些信息时用 search / finditer
  5. 调试技巧

    • 在调试复杂正则时,给关键分组命名(?P<name>),配合 m.groupdict() 使用,可以让代码更可读,也方便排查哪个组被捕获或为 None
  6. 性能注意

    • 只在必要范围内查找(传 start/end),可以减少不必要的扫描,提升处理流或长日志时的吞吐量。
http://www.dtcms.com/a/522614.html

相关文章:

  • 做传销网站违法的吗北京网站设计公司兴田德润信任高
  • 大型网站制作公司德阳做网站的公司
  • 奉新网站制作建设一个网站引言
  • 工会网站建设管理工作总结在哪个网站订酒店做申根签证
  • 淘宝网站网页设计说明seo教学免费课程霸屏
  • 成都网站制作报价专注wordpress主题
  • 电子商务网站的建设流程图河南建设厅官网证件查询入口
  • 鹤山区网站建设苏州网站建设新手
  • 重庆荣昌网站建设费用深圳形象设计公司
  • 本人已履行网站备案信息自己做的网站如何放到微信
  • 本地的赣州网站建设动易网站开发的主要技术
  • 免费怎么制作公司网站软件开发工程师介绍
  • 网站建设售后服务费包括哪些泉州有专门帮做网站的吗
  • 内江网站怎么做seo南宁网站设计方法
  • 农产品网站建设策划方案wordpress设置文章期限
  • 焦作做微信网站多少钱商标注册证查询网
  • 怎么建立自己公司的网站企业网站营销解决方案
  • 怎么知道网站是什么语言做的南京logo设计公司
  • 如何做企业网站小程序wordpress首页分类标题
  • 郑州专业做网站虚拟主机网站建设实训总结
  • 温州建设集团招聘信息网站上海网站建设助君网络7
  • 做海报用的图片网站东莞专业网站推广平台
  • 德阳市建设局官方网站管理手机网站
  • 八面通网站建设抖音挂小程序怎么赚钱
  • 济南高新网站建设曲阜人网站
  • 做网站的服务器多少钱一年百度小程序排名
  • 网站百度文库网站后台示演
  • 精神文明建设专题网站阐述网站建设的步骤
  • 济南高新区网站建设企业网站建设网站制作
  • 珠海企业免费建站wordpress 论坛编辑器