Python的字符串判断把我坑惨了,原来startswith和endswith的区别这么大

简介: 小刘误将`startswith`与`endswith`用反,导致路由逻辑全部失效:`/api/`应`startswith`,`.json`应`endswith`。二者语义相反——一个查开头,一个查结尾,用错不报错却静默走错分支。牢记名字即提示:start=开始,end=结束。(239字)

小刘那天写了一个静态文件服务器的路由逻辑,需要根据请求路径判断该交给谁处理。规则很简单:路径以 /api/ 开头的,交给 API 处理器;以 .json 结尾的,返回 JSON 内容;其他的当静态文件处理。

他写了一段代码:

def route(path):
   if path.endswith("/api/"):
       return handle_api(path)
   elif path.startswith(".json"):
       return serve_json(path)
   else:
       return serve_static(path)

跑起来之后,所有请求都走到了 serve_static,API 和 JSON 服务完全失效。他盯着代码看了半天,嘴里念叨:“/api/users 明明是以 /api/ 开头的啊,为什么 endswith 返回 False?”

旁边的同事探过头来,看了一眼说:“你把 startswith 和 endswith 用反了。/api/ 是前缀,应该用 startswith;.json 是后缀,应该用 endswith。”

小刘愣了一下,然后拍了下脑门:“我把这俩搞混了。”

这个错误听起来很蠢,但真到写代码的时候,很多人会因为赶时间、变量名相似、或者纯粹的手滑,把这两个方法用反。更麻烦的是,用反了通常不会报错,程序会静默地走错分支,直到你发现某个功能完全没生效。

代理 IP 使用小技巧 让你的数据抓取效率翻倍 (4).png

先看一个最直观的对比

s = "hello_world.py"

print(s.startswith("hello"))   # True
print(s.endswith("hello"))     # False

print(s.startswith(".py"))     # False
print(s.endswith(".py"))       # True

startswith 检查字符串是否以某个子串开头。endswith 检查字符串是否以某个子串结尾。一个看头,一个看尾,位置完全相反。

用一个生活场景来理解。你有一排人站在操场上,从左到右编号。

startswith 是问:“队伍最左边的人是不是叫张三?”

endswith 是问:“队伍最右边的人是不是叫李四?”

一个问题看队头,一个问题看队尾。问反了,答案自然就错了。

startswith:检查开头

startswith 的签名是 str.startswith(prefix[, start[, end]])。它检查字符串在指定范围内是否以 prefix 开头。

path = "/api/users"

print(path.startswith("/api/"))      # True
print(path.startswith("/api/user"))  # True
print(path.startswith("/admin"))     # False
print(path.startswith("api"))        # False,因为开头是 "/",不是 "a"

注意最后一个例子:path.startswith("api") 返回 False,因为字符串以 / 开头,不是以 a 开头。startswith 是严格的前缀匹配,从第一个字符开始比较。

startswith 可以接受一个元组作为参数,检查是否以其中任意一个开头:

filename = "photo.jpg"

print(filename.startswith((".jpg", ".png", ".gif")))  # True
print(filename.startswith((".txt", ".md")))            # False

这个元组参数的特性很多人不知道,导致写出这样的代码:

if filename.startswith(".jpg") or filename.startswith(".png") or filename.startswith(".gif"):
   ...

用元组可以写成一行:

if filename.startswith((".jpg", ".png", ".gif")):
   ...

endswith:检查结尾

endswith 的签名是 str.endswith(suffix[, start[, end]])。它检查字符串在指定范围内是否以 suffix 结尾。

filename = "report.pdf"

print(filename.endswith(".pdf"))    # True
print(filename.endswith(".doc"))    # False
print(filename.endswith("pdf"))     # True,没有点也匹配

注意最后一个例子:filename.endswith("pdf") 返回 True,因为 "report.pdf" 确实以 "pdf" 结尾。endswith 不关心你有没有写点,它只做字符串匹配。

这可能会带来一个隐患:如果你写 filename.endswith("pdf"),那么 "notpdf" 也会匹配,因为它以 "pdf" 结尾。通常我们应该写 filename.endswith(".pdf"),确保匹配的是扩展名,而不是文件名中随便出现的 pdf。

endswith 同样支持元组参数:

filename = "archive.tar.gz"

print(filename.endswith((".gz", ".zip", ".rar")))  # True
print(filename.endswith((".txt", ".md")))           # False

坑一:把 startswith 和 endswith 用反

这就是小刘踩的坑。前缀检查用了 endswith,后缀检查用了 startswith。

# 错误写法
if path.endswith("/api/"):     # 检查开头却用了 endswith
   handle_api(path)

if path.startswith(".json"):   # 检查结尾却用了 startswith
   serve_json(path)

这种错误不会报错,因为 endswith 和 startswith 都返回布尔值,语法上完全合法。程序会继续跑,只是逻辑全错了。

正确的写法:

if path.startswith("/api/"):
   handle_api(path)

if path.endswith(".json"):
   serve_json(path)

防坑的方法很简单:记住 start 是“开始”,end 是“结束”。startswith 看开头,endswith 看结尾。名字本身就是提示。

坑二:以为 startswith 会匹配任意位置

有些人把 startswith 和 in 搞混,以为 startswith 是“包含”的意思。

s = "hello world"
print(s.startswith("world"))  # False
print("world" in s)           # True

startswith 只检查开头,不检查中间。如果你想检查子串是否出现在任意位置,用 in。

同样,endswith 只检查结尾:

s = "hello world"
print(s.endswith("hello"))  # False
print("hello" in s)         # True

坑三:start 和 end 参数用错

startswith 和 endswith 都支持可选的 start 和 end 参数,用来限定检查范围。

s = "hello world"

print(s.startswith("world", 6))     # True,从索引 6 开始,以 "world" 开头
print(s.endswith("hello", 0, 5))    # True,在 [0, 5) 范围内,以 "hello" 结尾

注意 end 参数是开区间,不包含 end 位置。

s = "hello"
print(s.endswith("o", 0, 4))  # False,因为 end=4 不包含索引 4
print(s.endswith("o", 0, 5))  # True

这个细节容易搞混,但和切片的行为一致。

坑四:用 or 连接多个 startswith 调用

前面提到过,startswith 和 endswith 都支持元组参数。但很多人不知道,写成多个 or:

if filename.startswith(".jpg") or filename.startswith(".png") or filename.startswith(".gif"):
   ...

这样写逻辑是对的,但很啰嗦,而且每次都要重复 filename.startswith。用元组更简洁:

if filename.startswith((".jpg", ".png", ".gif")):
   ...

元组参数不仅更短,而且性能更好。因为只需要调用一次方法,而不是三次。

坑五:大小写敏感

startswith 和 endswith 是大小写敏感的。

filename = "Photo.JPG"

print(filename.endswith(".jpg"))  # False
print(filename.endswith(".JPG"))  # True

如果要做大小写不敏感的匹配,需要先转换:

filename.lower().endswith(".jpg")  # True

或者用正则表达式加 re.IGNORECASE。

坑六:路径分隔符在不同系统上不一样

如果你在用 startswith 或 endswith 判断路径,要小心不同操作系统的分隔符。

path = "C:\\Users\\Alice\\file.txt"  # Windows
print(path.endswith(".txt"))          # True
print(path.endswith("\\file.txt"))    # True
print(path.endswith("/file.txt"))     # False

Windows 用 \,Linux/macOS 用 /。如果你的代码需要跨平台,最好用 os.path 或 pathlib 来处理路径,而不是直接用字符串方法。

from pathlib import Path

p = Path("file.txt")
print(p.suffix == ".txt")  # True

性能:startswith 和 endswith 非常快

startswith 和 endswith 都是 O(k) 操作,k 是前缀或后缀的长度。它们不遍历整个字符串,只比较开头或结尾的固定长度。所以无论字符串多长,只要前缀/后缀长度固定,速度都很快。

import timeit

long_str = "a" * 1000000 + ".txt"

t1 = timeit.timeit(lambda: long_str.endswith(".txt"), number=1000000)
t2 = timeit.timeit(lambda: long_str[-4:] == ".txt", number=1000000)

print(f"endswith: {t1:.4f}")
print(f"切片比较: {t2:.4f}")

在我的机器上,endswith 大约 0.05 秒,切片比较大约 0.08 秒。endswith 略快,因为它是 C 实现的,不需要创建切片对象。

什么时候用 startswith,什么时候用 endswith?

判断标准很清晰:

检查开头:用 startswith。比如 URL 协议(http://、https://)、路径前缀(/api/)、命令前缀(git )。

检查结尾:用 endswith。比如文件扩展名(.py、.json)、域名后缀(.com、.org)、字符串后缀(;、})。

检查是否包含:用 in。startswith 和 endswith 都不检查中间。

检查多个前缀/后缀:用元组参数,不要写多个 or。

大小写不敏感:先 lower() 或 upper(),或者用正则。

实际应用场景

场景一:文件类型过滤

def is_image(filename):
   return filename.lower().endswith((".jpg", ".jpeg", ".png", ".gif", ".webp"))

场景二:URL 路由

def route(path):
   if path.startswith("/api/"):
       return handle_api(path)
   elif path.startswith("/static/"):
       return serve_static(path)
   else:
       return serve_index(path)

场景三:协议检查

def is_secure(url):
   return url.startswith("https://")

场景四:日志级别解析

def is_error(line):
   return line.startswith("ERROR") or line.startswith("CRITICAL")

场景五:字符串清理

def strip_quotes(s):
   if s.startswith('"') and s.endswith('"'):
       return s[1:-1]
   return s

回到小刘的故事

小刘最终的代码是这样的:

def route(path):
   if path.startswith("/api/"):
       return handle_api(path)
   elif path.endswith(".json"):
       return serve_json(path)
   else:
       return serve_static(path)

/api/users 正确进入了 API 处理器,data.json 正确返回了 JSON 内容。他把两个方法换回来之后,一切正常。

他后来在团队文档里写了一条:“startswith 看开头,endswith 看结尾。名字就是提示:start 是开始,end 是结束。别搞反了,搞反了不报错,但逻辑全错。”

这个错误听起来很低级,但真到写代码的时候,特别是在连续写了很多行判断逻辑之后,手滑把两个方法写反是常有的事。更麻烦的是,它不会报错,只会让程序静默地走错分支。

记住那个操场上排队的比喻:startswith 问的是队头,endswith 问的是队尾。你想问哪一头,就用哪个方法。

字符串判断本身不难,难的是在快速编码时保持对方法名的敏感度。startswith 和 endswith 的区别,不在于功能有多复杂,而在于位置完全相反。用对了,代码清晰高效;用反了,程序静默出错。

小刘后来养成了一个习惯:写 startswith 或 endswith 的时候,嘴里默念一遍“开头还是结尾”,确认无误再敲回车。这个习惯花不了两秒钟,但能省下一下午的调试时间。

目录
相关文章
|
5天前
|
云安全 人工智能 运维
当攻击全面Agent化,你的安全防御能否跟上AI Speed?
多起客户侧AK泄露事件应急的真实观察
1241 4
|
28天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
9782 32
|
8天前
|
存储 人工智能 安全
千问办公官网入口网页版登录:qwenwork.cn 注册送2000积分、登录送100积分
阿里云千问办公(QwenWork)是AI智能工作平台,支持一句话生成PPT、表格、网页、视频及数据分析。个人免费版送2000积分+每日登录赠100分,含1GB存储、5个发布页;企业版198元/席/月起。阿里千问办公QwenWork官网:https://t.aliyun.com/U/0VCTGt 阿里AI工作平台,一句话完成数据分析、PPT 生成、视频剪辑、网页搭建等复杂任务
|
3天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
|
14天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
5天前
|
Web App开发 人工智能 JavaScript
太猛了,一个月干到15W+,给大家推荐一个最近爆火的开源逆向工具:REA
REA(Reverse Engineer Anything)是近期爆火的开源AI逆向工具,4月发布,1个月内斩获15万+ Star。它让AI Agent无需源码即可分析二进制、APK、JS/Electron、HAR包等17类目标,自动反编译、生成可运行代码,并强制返回带证据与局限的结论。全本地执行,MIT协议,支持Hopper/Ghidra/IDA等引擎,一行命令即可上手。
621 1
太猛了,一个月干到15W+,给大家推荐一个最近爆火的开源逆向工具:REA
|
12天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型深度拆解:原理解读、实战测评与保姆级落地教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为**System One Model(系统一模型)**,对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
927 1
|
9天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型全解析:核心原理拆解,性能实战测评与落地部署教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为System One Model(系统一模型),对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
592 0

热门文章

最新文章