1. 先说清楚Python 3 里的字符串到底是什么1.1 str 是字符序列不是字节序列很多从 C 或 Java 转过来的人第一次看到 Python 的字符串会不适应。在 Python 3 里str是一个 Unicode 字符序列每个元素是一个“字符”而不是一个字节。这意味着len(你好)的结果是 2而不是 6你好[0]得到的是“你”这个字符而不是某个字节值。可以这样类比str是一排已经写好的字符卡片你关心的是一共有多少张卡片、每张卡片上写着什么字而bytes是这些卡片在电脑里的二进制编码你需要数的是每个字占多少个字节。Python 3 把这两者彻底分开文本处理用str文件读写和网络传输用bytes两者之间靠encode和decode来转换。这种设计带来的最大好处是你在代码里处理文本时基本不用关心底层是 UTF-8 还是 GBK。坏处是一旦你从文件、网络、数据库里拿到的是bytes就必须显式转成str否则会出现各种匪夷所思的报错比如TypeError: can only concatenate str (not bytes) to str。1.2 编码与解码绝大多数中文乱码的根源encode的方向是str - bytesdecode的方向是bytes - str。方向搞反是新手最常见的错误。我见过不少同学直接对一个bytes对象调用encode结果得到b...套b...越搞越乱。来看一段最基础的转换s 你好世界 b s.encode(utf-8) print(b) # b\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c text b.decode(utf-8) print(text s) # True实际项目里最常见的乱码场景是你用 UTF-8 写入文件但读文件时没有指定编码Python 在 Windows 上默认用了 GBK 去解码结果中文显示成一堆“锟斤拷”。“锟斤拷”这个经典乱码符号本质上就是 UTF-8 字节流被错误地按 GBK 解码后再重新编码产生的。应对方案其实很简单所有涉及文件读写的地方显式指定encodingutf-8。比如with open(data.txt, r, encodingutf-8) as f: content f.read()读取网页响应时也一样不要依赖默认编码拿到bytes后根据响应头里的charset或直接尝试 UTF-8 解码。如果确实不确定编码可以传errorsreplace这样遇到非法字节时会用占位符替换而不是直接抛出异常让你的程序崩溃起码能先跑起来再定位问题。1.3 不可变性拼接字符串为什么会越来越慢字符串是不可变对象这个特性经常被忽略直到你写出一个循环拼接的代码才发现性能不对劲。每次执行s partPython 都要创建一个新的字符串对象把旧内容复制过去再追加新内容。循环 10 万次就是 10 万次内存分配和复制时间复杂度接近 O(n²)。一个典型场景从数据库里查出 10 万行记录想拼成一个超大字符串用会让你怀疑机器是不是卡死了。正确做法是把所有片段收集到列表里最后用join一次性拼好parts [] for row in rows: parts.append(str(row[name])) result .join(parts)这里有个细节值得注意CPython 对做了优化当原字符串的引用计数为 1 时可以原地扩容而不复制所以小循环里不一定慢。但一旦字符串被另外的变量引用、或者被传进函数又传出来这个优化就会失效。为了养成稳定且可预测的习惯我建议大量拼接一律list.append加join不要赌解释器的优化行为。2. 高频操作工具箱切分、拼接、替换、修剪与查找2.1 split / rsplit / splitlines切分比你想的更有讲究在做文本处理时切分是最常用到的操作。不少从 C 语言转过来的同学会纠结“怎么按空格把字符串切开”在 C 里你得自己写循环在 Python 里一个split()就搞定了。但这里有个极其容易踩的细节**split()不传参数和传 是两个完全不同的行为**。s a b c print(s.split()) # [a, b, c] print(s.split( )) # [a, b, , c]不传参数时Python 会按任意连续的空白字符切分包括空格、制表符、换行并且自动过滤掉空串传 则是按单个空格字符精确切分连续空格之间会产生空字符串。a b c.split( )返回 4 个元素其中有一个空串如果你没意识到这点后面处理很容易出错。rsplit从右边开始切分在处理路径时特别好用比如file_path.rsplit(/, 1)可以把/home/user/data/a.txt拆成/home/user/data和a.txt。splitlines则专门按行界符切分能同时识别\n、\r\n等不同换行符比手动split(\n)更稳妥。如果你需要按多个分隔符切分比如同时按逗号和分号直接上正则re.split反而最省事import re print(re.split(r[,;], a,b;c)) # [a, b, c]2.2 join拼接字符串的标准姿势前面提到的join是把列表转成字符串的通用方法它不只是为了性能可读性也更好。-.join([2024, 05, 20])会得到2024-05-20比用去拼接清晰得多。这里提醒一句join里每个元素必须是字符串不能混入整数。如果你有一个[1, 2, 3]想拼成123直接.join([1, 2, 3])会报错需要先用列表推导转成str。这也是我在代码评审里经常看到的问题。再分享一个相对进阶的用法拼接 CSV 行时如果字段内容里可能包含逗号不能直接,.join(fields)。正确做法是先把含逗号、引号或换行的字段用双引号包起来内部的双引号再转成两个双引号。这个规则很多初学者不知道生成的文件用 Excel 打开就会错列。2.3 replace、translate 和大小写转换各管一摊replace处理简单替换非常方便s.replace(old, new)默认全部替换也可以传第三个参数限制替换次数。但它有个天然限制只能一次处理一种模式。如果你要同时把/替换成-、把空格替换成_写两行replace没问题但如果替换规则有十几个性能就不划算了。更高效的方式是使用translate配合str.maketrans建立字符映射表table str.maketrans({/: -, : _}) result a/b c.translate(table) # a-b_c大小写转换方面upper、lower是最常用的。capitalize只让首字母大写title会让每个单词首字母大写但注意英文标题里的冠词也会被大写不一定符合排版需求。如果你要做不区分大小写的比较建议优先用casefold而不是lower因为casefold做了更激进的折叠处理能正确处理德语ß这类特殊字符。2.4 strip 家族和查找 API清洗数据的日常数据清洗里离不开strip。 hello .strip()能去掉首尾空白strip(.,!)按字符集合去掉首尾的标点注意这里传的是字符集合不是子串所以abcabc.strip(abc)结果是空串因为首尾所有 a、b、c 都被去掉了。查找方面find返回子串第一次出现的索引找不到时返回-1index行为和find一样但找不到会抛ValueError。到底用哪个取决于你是否想捕获异常。我的习惯是判断是否存在用in或find想直接拿索引且确定存在时用index。Python 3.9 还引入了两个很实用的小方法removeprefix和removesuffix专门用来去掉前缀和后缀。以前判断s.startswith(abc)后再切片现在一行搞定代码可读性强很多。3. 跨界转换字符串与数字、日期、JSON 之间的翻译规则3.1 字符串转数字别被 isdigit 骗了很多从数据库迁移过来的需求里都有“判断某个字段能否转成数字”这一步比如从 SQL Server、DB2 或者 Oracle 里导出数据时需要过滤掉不可转数字的字符串。在 Python 里最直接的方式是int(s)和float(s)但很多人会先想到isdigit()然后就踩坑了。isdigit()只能判断字符串是否全部由数字字符组成它不认识正负号和小数点。-3.isdigit()是False3.14.isdigit()也是False。所以在做通用数字判断时不要迷信isdigit最稳妥的办法是直接尝试转换并用异常兜底def to_number(s): try: return float(s) except (TypeError, ValueError): return None这样写的好处是3.14、-3、1e5都能被正确识别为数字而空字符串、.、abc则返回None。如果你确实需要限定格式比如只接受非负整数再用s.isdigit()如果允许小数且不接受科学计数法可以用re.fullmatch(r[-]?\d(\.\d)?, s)先做格式校验。业务需求不同判断策略就不同最好不要一个isdigit走天下。3.2 数字转字符串格式化输出是基本功反过来把数字转成字符串str(number)自然是最简单的。但业务里需要的是“输出成什么样”这就轮到格式化出场。f-string 是 Python 3.6 以后我最常用的方式price 3.1415926 print(f{price:.2f}) # 3.14 print(f{1234567:,}) # 1,234,567 print(f{42:05d}) # 00042保留两位小数、千分位分隔、补零对齐一行全部搞定。老项目里可能还会见到%占位符和str.format的写法它们能力是等价的只是可读性不如 f-string。如果你在维护老代码看得懂这两种写法就行新代码建议统一用 f-string。3.3 JSON 与字符串互转注意中文转义问题字符串里嵌 JSON 的情况太常见了。API 返回值是 JSON 字符串你拿过来要json.loads解析要调别人接口时又要json.dumps把字典转成字符串。这里有一个非常容易忽略的坑**json.dumps默认会把中文转成\uXXXX的形式**。import json data {name: 张三} print(json.dumps(data)) # {name: \u5f20\u4e09} print(json.dumps(data, ensure_asciiFalse)) # {name: 张三}从功能上讲\u5f20\u4e09和“张三”在 JSON 里是等价的但你不希望把这种转义后的内容直接展示给用户也不方便排查问题。所以凡是需要把字典转成 JSON 字符串我基本都会带上ensure_asciiFalse。在json.loads解析时建议只捕获json.JSONDecodeError而不抓裸的Exception这样解析失败时你能快速定位到是哪一段 JSON 格式有问题。3.4 日期字符串的解析与格式化占位符别张冠李戴数据库里的日期字段经常是字符串比如2024-05-20 08:30:00要用 Python 处理就得先转成datetimefrom datetime import datetime ts 2024-05-20 08:30:00 dt datetime.strptime(ts, %Y-%m-%d %H:%M:%S)反向输出时用strftime。这两个方法的占位符特别容易踩坑尤其是%Y和%y的区别%Y是四位年份%y是两位年份%m是月份%M是分钟%H是 24 小时制%I是 12 小时制。我见过不止一次把%M当成月份用导致解析结果不对的情况。如果日期格式比较乱比如用户输入2024/05/20或05-20-2024多准备几个格式模板按顺序尝试解析会比写一个超复杂的正则更稳定。4. 匹配与提取从“包含判断”到“正则表达式”4.1 in、startswith、endswith判断包含的最快路径判断一个字符串是否包含某个子串最简单的写法是if abc in s:。这个操作是 C 语言层面的快速匹配性能很好。判断开头结尾则用startswith和endswith它们还支持传元组比如s.endswith((.jpg, .png))可以一次性判断多个后缀。如果要做不区分大小写的包含判断很多人会写if abc in s.lower():这在大多数场景下没问题但要注意lower()会生成一个全新的字符串。如果s特别大或者这个判断在循环里要执行几十万次可以考虑先s s.casefold()一次再做包含判断避免每次循环都创建新字符串。startswith和endswith本身支持指定起始和结束位置比如s.startswith(abc, 5)只检查索引 5 开头的位置灵活但容易被人忽略。4.2 正则表达式提取中间内容的正确姿势热搜词里有“c# 正则表达式 提取出中间的数字及#符号后的字符串”这个需求在 Python 里用re模块很直接。假设文本是订单号: #10086abc你想提取#后面的数字10086和字母abc可以这样写import re text 订单号: #10086abc pattern re.compile(r#(\d)([A-Za-z])) m pattern.search(text) if m: print(m.group(1)) # 10086 print(m.group(2)) # abc这里的关键点是捕获组。正则里用括号括起来的部分会被单独记住m.group(0)是整个匹配结果m.group(1)、m.group(2)分别是第一个和第二个括号里的内容。如果要提取所有匹配项用pattern.findall(text)返回的是一个元组列表。正则书写时还有个常见问题\d默认是贪婪匹配如果目标文字是#10086abc123(\d)会把10086全部吃掉如果你只想要100就要用(\d?)非贪婪模式。这个细节很容易被忽略建议在写正则时先想清楚匹配的边界。4.3 什么时候不该用正则正则虽然强大但过度使用会严重影响可读性和性能。如果一个需求用in或startswith几行就能解决就完全没有必要上正则。比如判断邮箱格式、提取 URL 参数这类既有结构又有边界的场景正则确实是好工具但如果你只是想知道字符串里有没有一个固定的词用in显然更直观。另外正则表达式在 Python 里每次调用re.search/re.findall时如果没有预编译解释器会临时编译一次。如果这个正则要在循环里用几千次提前re.compile能显著提升性能。从可维护性角度讲把正则也抽成变量并起个有意义的名字后续看代码时能少掉不少头发。5. 排序、格式化与空值三个容易写出隐患的地方5.1 字符串排序默认排序结果可能和你想象的不一样字符串排序的问题容易被忽视直到你发现sorted()的结果不符合预期。Python 默认按 Unicode 码点排序而大写字母的码点在小写字母之前所以Apple、Banana这种大写开头的字符串会全部排在小写开头的单词前面即使按字母表的习惯它们应该混在一起。words [banana, Apple, apple, Banana] print(sorted(words)) # [Apple, Banana, apple, banana]如果这组数据是给人看的通常需要忽略大小写排序只要加一个keystr.lower参数print(sorted(words, keystr.lower)) # [Apple, apple, Banana, banana]注意keystr.lower传的是函数本身不是调用结果这是一个 Python 里很常见的写法。中文排序又是另一个坑默认按码点排序并不是按拼音。如果你需要按拼音排中文列表最简单的方式是安装pypinyin库用拼音作为排序关键字比如sorted(names, keylambda x: pypinyin.lazy_pinyin(x))否则默认结果会让人摸不着头脑。5.2 三种格式化方式怎么选Python 字符串格式化主要有三种写法%占位符、str.format和 f-string。%写法从 Python 2 延续至今常见于日志模块比如result: %s % valuestr.format适用于模板配置场景尤其是模板从外部读进来时比较安全f-string 最现代也最直观直接在字符串里写表达式性能和可读性都很好。但 f-string 有一个场景不能用延迟求值。比如日志库的写法import logging logger logging.getLogger(app) # 不推荐先格式化再传参s 永远会被计算 logger.info(f处理完成耗时 {cost:.2f}s) # 推荐传给日志库做惰性格式化 logger.info(处理完成耗时 %.2fs, cost)第一条语句不管日志级别是否过滤都会执行字符串格式化第二条语句只有当日志真正要输出时才会格式化。在高频日志场景里这个差异对性能是有明确影响的。5.3 空字符串与 None 的判断很容易混检查字符串是否为空最 Pythonic 的写法是if not s:它能把空字符串和None一起判掉。但有些场景需要区分“没传”和“传了空字符串”。比如一个接口参数None表示没有传表示用户主动清空了内容两者处理逻辑完全不同。这时就不能用if not s:而要显式写if s is None:和if s :。还有个常见陷阱是s or default。如果s是空字符串这个表达式也会返回默认值。如果你只想在s为None时用默认值、空字符串要保留千万不要用or应该写value default if s is None else s另外比较字符串是否相等用不要用is。is比较的是对象身份不是内容。虽然 CPython 对小字符串有缓存短字符串相等时is偶尔也会返回True但这个行为完全不可依赖。我见过有同事因为abc is abc恰好是True就以为is可以比较字符串结果上线后在处理长字符串时出了线上故障。6. 养成这几个习惯之后字符串处理很少再出问题前面讲了很多具体知识点最后结合我带项目的经验分享几条我自己会遵守的习惯。第一外部输入先统一类型。不管是文件读取、网络请求还是数据库查询先明确拿到的到底是str还是bytes统一转成str再进入业务逻辑。与其在十几个地方分别处理编码问题不如在入口统一解决。第二数字转换全部走 try/except。不要写“先用 isdigit 判断再 int 转换”的代码因为判断逻辑很难覆盖所有边界。直接尝试转换、失败返回None或抛自定义异常代码更短更稳。第三大量拼接优先list.appendjoin。这条不仅仅是为了性能也是为了让你的数据流更清晰——先收集再统一输出中间排查问题时也更方便。第四正则要编译并命名。所有正则表达式都用re.compile编译后放到变量里名字起清楚。这样代码跑起来会快一些更重要的是其他人读代码时能一眼看出这个正则的意图。第五测试边界条件。写字符串处理函数时至少测一下空字符串、纯空格、包含中文、包含 emoji 的情况。中文占 3 个字节但只算 1 个字符emoji 占 4 个字节但也只算 1 个字符很多人在这一点上吃过亏。我自己在写字符串处理工具时有个固定的三件套流程先确认输入类型和编码再用一个函数封装核心转换逻辑并加上类型注解最后写一组边界测试用例。这套流程看着朴素但确实让我在字符串这上面踩过的坑越来越少。如果你正被某个诡异的字符串问题折磨不妨也按这个思路排查一遍大概率能把问题定位到“类型没统一”或“编码没指定”这两个源头上。
企业数字化 ERP 产品动态
相关推荐
Windows安全加固基础:账号、补丁、端口与日志四步走 先说结论:Windows的安全加固并不神秘,说到底是把账号、系统、网络、日志这四条线捋清楚。很多人一听到“安全”就想到装杀毒软件、装防火墙,实际上大部分真实入侵靠的都是弱口令、未打补丁、暴露了不该暴露的端口,以及出了事没有日… · 2026/9/26 4:48:55
Java同城租房系统实战:Spring Boot+Vue前后端分离完整项目 做同城租房系统这活儿,说实话,看着简单,真正要把房源、订单、用户、支付这几条线拧在一起,还是挺考验人的。尤其是用 Java 这套技术栈从零到一个能跑起来的完整项目,里面的坑和细节,不亲手走一遍根本意识不… · 2026/9/26 4:48:55
WorkBuddy自动化办公实战:从安装到OpenClaw技能兼容全解析 1. 为什么我要折腾 WorkBuddy 这套自动化办公方案第一次听到 WorkBuddy 这个名字,是在一个做跨境电商的朋友群里。有人丢了一张截图,说用 WorkBuddy 把每天要花两小时的商品上架流程压缩到了三分钟,底下立刻炸出一堆人问怎么装的。我当时的第… · 2026/9/26 4:48:55
华为擎云L420X/L540X装Windows实战:ARM64跨架构迁移的坑与解 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 5:24:47
数据结构上机实验避坑指南:线性表、栈、队列与二叉树C语言实现 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 5:24:47
I2C总线从物理层到协议层彻底解析:开漏、仲裁、时钟拉伸与实战避坑 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 5:24:41
数据中心U位资产管理:从人工台账到自动识别方案 1. 机房里的头等大事:U位管理到底是什么1.1 一个真实场景带出痛点先说个我亲自踩过的坑。前几年接手一个中型机房,总共四十多个机柜,设备大概八百多台。前任运维离职时留下一个Excel表,里面登记了每台服务器的U位、IP、序列号、维… · 2026/9/26 5:24:41
树莓派picamera与PC实时视频传输:Socket协议设计与性能优化 1. 项目缘起与整体方案设计1.1 为什么会有这个需求手里攒了一块树莓派和几个摄像头模块,最开始只是想做个简单的监控,看看家里没人时猫在干什么。但真正动手之后发现,树莓派本地存视频、本地看画面这件事限制太多——SD卡写入寿命有限&#x… · 2026/9/26 5:24:41
Nmap网络扫描原理与实战:从入门到网工必备技能 1. 为什么“网工入门第一课”不是学IP地址,而是Nmap?刚入行那会儿,我被安排去给客户做一次基础网络健康检查。客户只提了一个要求:“帮我看看这台防火墙后面,到底连着几台设备?哪些端口开着?有没… · 2026/9/26 5:24:35
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46