Python 系列(六) 字符串处理实战,查找、替换、切片与日志分析
1 | 作者:李晓辉 |
- Python 系列(一) 从零开始认识 Python,入门其实没那么难
- Python 系列(二) 学 Python 的第一步,把开发环境搭建好
- Python 系列(三) 基础语法避坑指南,缩进、大小写、续行与注释
- Python 系列(四) Python 输入输出详解,print()、input() 与常见避坑指南
- Python 系列(五) 数值类型与运算符详解,整除、浮点精度与进制转换实战
- Python 系列(六) 字符串处理实战,查找、替换、切片与日志分析
- Python 系列(七) 类型转换详解,int()、float()、str() 与金额计算避坑
为什么 'Hello' + 'World' 可以拼接,而 'Hello' * 3 却不是把字符乘以 3?为什么字符串创建后,不能直接修改其中的一个字符?为什么日志里用 find() 查找关键字,找不到时会返回 -1,而不是直接报错?
这些问题看起来基础,却是处理日志、配置文件、URL 时最容易踩坑的地方。这篇文章我们就从字符串的本质开始,逐步搞懂字符串的创建、常用方法和基本操作,再通过几个运维场景把知识真正用起来。
上一篇我们学习了 Python 数值类型和运算符,掌握了整数、浮点数、整除和幂运算。现在,程序已经能计算了。接下来要解决的问题是:如何正确地读取、处理和输出文本?
Python 里的字符串是什么?
Python 中的字符串(String)是由零个或多个字符组成的不可变序列,使用 str 类型表示。
这里有两个关键词:序列和不可变。
序列意味着字符串中的字符有先后顺序,可以通过索引访问,也可以使用切片截取其中一部分。
不可变意味着字符串对象创建后,不能直接修改其中的某个字符。如果需要改变文本内容,通常要通过拼接、替换等操作创建新的字符串。
例如:
1 | s = "hello" |
运行结果:
1 | hello |
为什么会报错?因为 Python 不允许直接修改字符串中的单个字符。
如果想把 hello 变成 Hello,可以这样做:
1 | s = "hello" |
运行结果:
1 | Hello |
这里并不是修改了原字符串,而是创建了一个新字符串,然后让变量 s 指向新字符串。
字符串不可变有助于减少共享文本被意外修改的问题,但这并不意味着整个程序自动具备线程安全性。
怎么创建字符串?
单引号和双引号
Python 可以使用单引号或双引号创建字符串,两者没有本质区别。
1 | a = "This is a literal string" |
运行结果:
1 | This is a literal string and so is this |
如果字符串中包含引号,可以选择另一种引号包裹字符串,减少转义符的使用。
1 | print('"Double quotes" inside of single quotes') |
运行结果:
1 | "Double quotes" inside of single quotes |
多行字符串
如果字符串内容比较长,或者需要保留换行,可以使用三引号。
1 | message = """Royal Straight Flush |
运行结果:
1 | Royal Straight Flush |
Python 还支持使用反斜杠 \ 连接跨行的普通字符串:
1 | message = "This is a long string just to " \ |
运行结果:
1 | This is a long string just to illustrate continuation |
注意,反斜杠续行会连接代码行,不会自动在字符串中插入换行符。如果需要保留换行,使用三引号或显式写入 \n 更合适。
原始字符串
在 Windows 路径、正则表达式等场景中,经常会遇到反斜杠。
例如:
1 | path = "C:\\Users\\xiaohui\\Documents" |
输出:
1 | C:\Users\xiaohui\Documents |
因为普通字符串中的反斜杠可能参与转义,所以需要写成 \\。
如果使用原始字符串,可以让反斜杠不按普通字符串的方式解释:
1 | path = r"C:\Users\xiaohui\Documents" |
输出:
1 | C:\Users\xiaohui\Documents |
原始字符串使用 r 或 R 前缀,常用于 Windows 路径和正则表达式。
不过,原始字符串并不意味着反斜杠完全不受语法限制。例如,下面的写法会导致语法错误:
1 | # 错误示例:原始字符串不能以单个反斜杠结尾 |
常见转义序列
普通字符串中的反斜杠可以引出转义序列。
| 转义序列 | 含义 |
|---|---|
\n | 换行 |
\t | 水平制表符 |
\\ | 反斜杠 |
\' | 单引号 |
\" | 双引号 |
\xhh | 两位十六进制数表示的字符 |
\uXXXX | 四位十六进制数表示的 Unicode 码位 |
\UXXXXXXXX | 八位十六进制数表示的 Unicode 码位 |
例如:
1 | print("第一行\n第二行") |
运行结果:
1 | 第一行 |
其中,\x41 表示十六进制数 41 对应的字符,也就是字母 A。
常用字符串方法
Python 提供了很多字符串方法,可以用来判断文本内容、查找关键字、替换文本、分割字符串以及清理空白字符。
需要记住一点:字符串不可变,因此大多数字符串处理方法会返回处理后的新字符串,而不是直接修改原字符串。
判断类方法
先来看一个常见场景:检查 URL 的前缀和后缀。
1 | url = "www.linuxcenter.cn" |
运行结果:
1 | True |
这里用到了三个方法:
startswith():判断字符串是否以指定内容开头。endswith():判断字符串是否以指定内容结尾。upper():返回转换为大写后的新字符串。
可以看到,调用 upper() 后,原来的 url 仍然是小写。这正是字符串不可变的体现。
startswith() 和 endswith() 还可以接收元组,一次检查多个前缀或后缀:
1 | filename = "server.log" |
运行结果:
1 | True |
在检查文件类型、URL 前缀或日志格式时,这种写法很方便。
判断字符的类型
Python 还提供了一组用于判断字符串内容的方法。
1 | print("AbCDe".isalpha(), "AbCd123".isalpha()) |
运行结果:
1 | True False |
这些方法分别表示:
isalpha():字符串中的字符是否全部为字母。isnumeric():字符串中的字符是否全部属于 Unicode 数字字符。isspace():字符串中的字符是否全部为空白字符。isupper():字符串中是否存在区分大小写的字符,且这些字符全部为大写。
注意,这些方法并不等同于完整的业务校验。例如,isnumeric() 返回 True,不代表这个字符串一定能直接转换成十进制整数;isalpha() 也不代表字符串只能包含英文字母。
查找关键字
处理日志时,经常需要查找某个关键字是否存在。
1 | word = "is" |
运行结果:
1 | First: 16 |
这里用到了两个方法:
find():返回子字符串第一次出现的位置,也可以指定开始查找的位置。count():统计子字符串出现的次数。
需要特别注意,find() 找不到内容时不会抛出异常,而是返回 -1。
1 | log = "2026-10-14 INFO service started" |
运行结果:
1 | 11 |
-1 表示没有找到。如果直接把这个结果用于索引或切片计算,可能导致错误的业务逻辑。
例如,下面的代码就存在问题:
1 | log = "service started" |
因为 position 是 -1,所以程序会取出字符串最后一个字符,而不是报错。
更稳妥的写法是先检查返回值:
1 | log = "service started" |
如果希望查找失败时抛出 ValueError,也可以使用 index()。
对齐与替换
有时我们需要把文本对齐,或者将某些内容替换成新的内容。
1 | word = "is" |
运行结果:
1 | Right Justified: is | |
其中:
rjust():在左侧填充字符,让字符串达到指定宽度。ljust():在右侧填充字符,让字符串达到指定宽度。replace():将指定的子字符串替换为新内容。
replace() 的第三个参数表示最多替换多少次。如果省略,就会替换所有匹配内容。
注意,replace() 不会直接修改原字符串。如果需要使用替换结果,应当接收返回值:
1 | text = "hello world" |
输出仍然是:
1 | hello world |
正确的写法是:
1 | text = "hello world" |
输出:
1 | Hi world |
分割字符串
在运维脚本中,我们经常需要把一行文本拆分成多个字段。
1 | data = "1 4 1 1abc" |
运行结果:
1 | ['1', '4', '1', '1abc'] |
split() 会返回一个列表(list),而不是字符串。
这里还有一个很容易踩坑的地方:split(" ") 和 split() 并不完全一样。
1 | data = "nginx active\t running" |
运行结果:
1 | ['nginx', '', '', 'active\t', 'running'] |
区别在于:
split(" "):只按普通空格分割,连续空格可能产生空字符串,制表符不会作为分隔符。split():不指定分隔符时,按连续的空白字符分割,并忽略字符串两端的空白。
因此,处理空格数量不固定的文本时,通常优先考虑 split()。
不过,日志格式如果有固定字段规则,不能只依赖简单分割。例如,最后一个字段可能包含空格,这时可以指定最多分割次数:
1 | log = "2026-10-14 12:00:01 ERROR disk full" |
运行结果:
1 | ['2026-10-14', '12:00:01', 'ERROR', 'disk full'] |
这样就能保留最后的错误信息,不会把 disk full 拆成两个字段。
去除空白字符
读取文件、接收用户输入或处理日志时,字符串两端经常会有多余的空格、换行符或制表符。
Python 提供了三个常用方法:
strip():去除两端的空白字符。lstrip():去除左侧的空白字符。rstrip():去除右侧的空白字符。
看一个例子:
1 | data = "\t \nabc def\t \n" |
运行结果:
1 | 16 : 8 |
为什么 strip() 之后长度变成了 8?
因为 abc def 中间的两个空格会保留,只有字符串两端的空白字符被去除了。
还需要注意,strip() 默认会识别许多 Unicode 空白字符,通常也包括中文全角空格,并不只是普通的 ASCII 空格。
如果要清理指定字符,可以给方法传入参数:
1 | text = "###hello###" |
运行结果:
1 | hello |
这里的 strip("#") 会去除两端的 #,但不会修改中间的字符。
另外,strip("#abc") 的含义也不是删除完整的字符串 #abc,而是从两端不断去除属于 #abc 这个字符集合的字符。这一点在清理文本时要特别注意。
序列操作:拼接、重复与成员测试
字符串是一种序列,因此可以使用一些通用的序列操作。
字符串拼接
最常见的方式是使用 +:
1 | first_name = "Casey" |
运行结果:
1 | Casey Jackson |
不过,Python 还有一种特殊语法:相邻的字符串字面量可以自动拼接,即使中间没有 +。
1 | full_name = "Casey" " " "Jackson" |
运行结果:
1 | Casey Jackson |
注意,这种自动拼接只适用于相邻的字符串字面量,不适用于运行时变量。
例如:
1 | a = "Casey" |
字符串重复
使用 * 可以重复字符串。
1 | stars = "*" * 12 |
运行结果:
1 | ************ : ##### |
这里的 * 并不是把每个字符的数值乘以指定数字,而是将整个字符串重复指定次数。
所以:
1 | print("Hello" + "World") |
运行结果:
1 | HelloWorld |
这也是为什么字符串可以使用 *,却不具有数值乘法的含义。
成员测试
使用 in 可以判断一个字符或子字符串是否存在于字符串中。
1 | x = "Hello there" |
运行结果:
1 | True True False |
这里的匹配区分大小写,所以 "hell" 与 "Hell" 并不相同。
在运维场景中,可以用 in 判断日志是否包含某个关键字:
1 | log = "2026-10-14 ERROR disk full" |
运行结果:
1 | 发现错误日志 |
对于只需要判断内容是否存在的场景,in 通常比先调用 find() 再判断返回值更加直观。
索引与切片
既然字符串是序列,就可以通过索引访问字符,通过切片截取部分内容。
1 | spam = "Spam and eggs" |
运行结果:
1 | S | m | s |
这里需要记住:
- 索引从
0开始。 - 负数索引从字符串末尾开始,
-1表示最后一个字符。 - 切片采用左闭右开区间,包含起始位置,不包含结束位置。
- 省略起始位置表示从开头开始,省略结束位置表示一直取到末尾。
切片的基本形式是:
1 | s[start:end:step] |
其中,step 表示步长。
例如:
1 | alphabet = "abcdefghijklmnopqrstuvwxyz" |
运行结果:
1 | cfilor |
从索引 2 开始,每隔三个位置取一个字符,直到索引 18 之前结束。
切片还有一个很实用的特点:超出字符串范围的结束索引通常不会报错,而是截取到字符串实际结束的位置。
1 | text = "hello" |
运行结果:
1 | ello |
但要注意,直接访问不存在的索引,例如 text[100],仍然会触发 IndexError。
字符串格式化
当我们需要把变量放进提示信息、日志或报告中时,就会用到字符串格式化。
Python 支持多种格式化方式。现代 Python 代码中,推荐优先使用 f-string;format() 和 % 格式化在已有项目中也很常见。
使用 f-string
1 | name = "nginx" |
运行结果:
1 | 服务 nginx 当前状态:active |
f-string 使用字符串前缀 f,可以直接在字符串中通过 {} 插入变量或表达式。
例如,在运维脚本中输出磁盘使用率:
1 | disk = "/dev/sda1" |
运行结果:
1 | 磁盘 /dev/sda1 当前使用率:82.57% |
其中,.2f 表示按照浮点数格式保留两位小数。
使用 format()
format() 同样可以将变量插入字符串。
1 | name = "John" |
运行结果:
1 | First: John Last Name: Smith Middle Initial: C. |
如果使用关键字参数,代码会更加清晰:
1 | dimensions = "Type: {type}\nHeight: {height}, Width: {width}" |
运行结果:
1 | Type: Box |
字符串格式化方式对比
1 | name = "nginx" |
三种方式都可以完成格式化,但在编写新的 Python 代码时,f-string 通常更简洁直观。
字符串处理流程
处理文本时,我们通常会经历创建、清理、查找、分割和格式化等步骤。
flowchart LR
A[原始文本] --> B[创建与转义]
B --> C[清理空白字符]
C --> D[查找与分割]
D --> E[拼接或替换]
E --> F[格式化输出]例如,读取一行日志后,可以先去掉首尾空白,再判断是否包含错误关键字,最后将需要的信息提取出来。
这类操作是 Python 自动化运维脚本中的常见基础。
生产环境常见坑
字符串的基本语法并不复杂,但在真实的运维场景中,仍然有几个容易出错的地方。
- 字符串不可变。
replace()、upper()等方法通常返回新字符串。如果需要使用处理后的结果,记得接收返回值。 find()找不到会返回-1。 不要直接将返回值当作有效索引使用,应先判断是否找到。split(" ")与split()不同。 日志中的空格数量不固定时,直接使用split(" ")可能产生空字符串;需要根据输入格式选择合适的分割方式。strip()只处理两端。 它不会自动删除字符串中间的空白,也不会自动清理任意标点符号。- 大量字符串拼接需要注意性能。 循环中反复使用
+可能产生较多中间字符串对象;如果需要拼接大量文本,可以先收集到列表,再使用join()。 - 字符串匹配通常区分大小写。 比如
"ERROR" in log不会匹配小写的"error"。如果业务要求忽略大小写,可以先使用lower()或casefold()统一处理。 - 简单字符串操作不等于完整的日志解析。 如果日志包含引号、转义字符或复杂字段,应根据实际格式选择更合适的解析方法。
使用 join() 拼接大量字符串
例如,需要把多个服务名称拼接成一行:
1 | services = ["nginx", "sshd", "crond"] |
运行结果:
1 | nginx, sshd, crond |
join() 会使用指定的分隔符连接可迭代对象中的字符串。这里的分隔符是 ", "。
需要注意,参与拼接的元素必须是字符串。如果列表中包含整数等其他类型,需要先转换为字符串。
综合练习:处理一条运维日志
假设我们有这样一条日志:
1 | 2025-10-28 12:00:01 ERROR disk full from 192.168.1.10 |
现在需要完成几个任务:
- 去掉日志首尾的空白字符。
- 提取日期、时间、日志级别和消息内容。
- 判断日志是否为
ERROR。 - 将消息中的 IP 地址替换为脱敏标记。
- 使用 f-string 输出整理后的结果。
可以这样实现:
1 | log = " 2025-10-28 12:00:01 ERROR disk full from 192.168.1.10 " |
运行结果:
1 | 发现错误日志 |
这个示例展示了字符串处理的基本流程。不过,示例中的 IP 脱敏使用的是精确文本替换,只适用于已知的固定 IP。如果需要批量脱敏不同 IP,或者处理复杂的日志格式,可以进一步学习 Python 正则表达式和日志解析方法。
写在最后
这一篇我们从字符串的本质出发,学习了 Python 文本处理的基础知识:
- 字符串的不可变特性,以及单引号、双引号、三引号、原始字符串和转义序列。
startswith()、endswith()、find()、replace()、split()、strip()等常用方法。- 字符串拼接、重复、成员测试、索引和切片。
- 使用 f-string 和
format()格式化输出。 - 将字符串操作应用到日志字段提取、关键字判断和 IP 脱敏等运维场景。
对于 Linux 运维人员来说,字符串处理是编写自动化脚本的重要基础。无论是分析 Nginx 日志、读取配置文件,还是调用云平台 API,都会频繁用到这些操作。
