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

【Python Cookbook】字符串和文本(二)

字符串和文本(二)

  • 6.字符串忽略大小写的搜索替换
  • 7.最短匹配模式
  • 8.多行匹配模式
  • 9.将 Unicode 文本标准化
  • 10.在正则式中使用 Unicode

6.字符串忽略大小写的搜索替换

你需要以忽略大小写的方式搜索与替换文本字符串。

为了在文本操作时忽略大小写,你需要在使用 re 模块的时候给这些操作提供 re.IGNORECASE 标志参数。比如:

>>> text = 'UPPER PYTHON, lower python, Mixed Python'
>>> re.findall('python', text, flags=re.IGNORECASE)
['PYTHON', 'python', 'Python']
>>> re.sub('python', 'snake', text, flags=re.IGNORECASE)
'UPPER snake, lower snake, Mixed snake'

最后的那个例子揭示了一个小缺陷,替换字符串并不会自动跟被匹配字符串的大小写保持一致。为了修复这个,你可能需要一个辅助函数,就像下面的这样:

def matchcase(word):def replace(m):text = m.group()if text.isupper():return word.upper()elif text.islower():return word.lower()elif text[0].isupper():return word.capitalize()else:return wordreturn replace
  • matchcase(word) 函数:
    • 这是一个高阶函数,它返回另一个函数 replace(m)
    • word 参数是要替换的目标词(这里是 'snake')。
  • replace(m) 函数:
    • m 是一个正则匹配对象。
    • m.group() 返回匹配到的完整文本(例如 'Python', 'PYTHON', 'python' 等)。
    • 这个函数检查匹配文本的大小写格式,并返回 word 的相应大小写形式:
      • 如果匹配文本是全大写(如 'PYTHON'),返回 word.upper()(即 'SNAKE')。
      • 如果匹配文本是全小写(如 'python'),返回 word.lower()(即 'snake')。
      • 如果匹配文本是首字母大写(如 'Python'),返回 word.capitalize()(即 'Snake')。
      • 否则,直接返回 word

下面是使用上述函数的方法:

>>> re.sub('python', matchcase('snake'), text, flags=re.IGNORECASE)
'UPPER SNAKE, lower snake, Mixed Snake'
  • matchcase('snake') 先执行,返回 replace 函数,此时 word = 'snake'
  • re.sub 遍历 text,找到所有匹配 'python'(不区分大小写)的子串。
  • 对每个匹配项,调用 replace(m),并根据匹配文本的大小写格式返回 'snake' 的对应形式。
  • re.sub 把所有匹配项替换成 replace 返回的结果。

🚀 matchcase('snake') 返回了一个回调函数(参数必须是 match 对象,前面一节提到过,sub() 函数除了接受替换字符串外,还能接受一个回调函数。

对于一般的忽略大小写的匹配操作,简单的传递一个 re.IGNORECASE 标志参数就已经足够了。但是需要注意的是,这个对于某些需要大小写转换的 Unicode 匹配可能还不够。

7.最短匹配模式

你正在试着用正则表达式匹配某个文本模式,但是它找到的是模式的最长可能匹配。而你想修改它变成查找最短的可能匹配。

这个问题一般出现在需要匹配一对分隔符之间的文本的时候(比如引号包含的字符串)。为了说明清楚,考虑如下的例子:

>>> str_pat = re.compile(r'"(.*)"')
>>> text1 = 'Computer says "no."'
>>> str_pat.findall(text1)
['no.']
>>> text2 = 'Computer says "no." Phone says "yes."'
>>> str_pat.findall(text2)
['no." Phone says "yes.']

在这个例子中,模式 r'\"(.*)\"' 的意图是 匹配被双引号包含的文本。 但是在正则表达式中 * 操作符是贪婪的,因此匹配操作会查找最长的可能匹配。于是在第二个例子中搜索 text2 的时候返回结果并不是我们想要的。

为了修正这个问题,可以在模式中的 * 操作符后面加上 ? 修饰符,就像这样:

>>> str_pat = re.compile(r'"(.*?)"')
>>> str_pat.findall(text2)
['no.', 'yes.']

这样就使得匹配变成非贪婪模式,从而得到最短的匹配,也就是我们想要的结果。

这一节展示了在写包含点 . 字符的正则表达式的时候遇到的一些常见问题。在一个模式字符串中,点 . 匹配除了换行外的任何字符。然而,如果你将点 . 号放在开始与结束符(比如引号)之间的时候,那么匹配操作会查找符合模式的最长可能匹配。这样通常会导致很多中间的被开始与结束符包含的文本被忽略掉,并最终被包含在匹配结果字符串中返回。通过在 * 或者 + 这样的操作符后面添加一个 ? 可以强制匹配算法改成寻找最短的可能匹配。

8.多行匹配模式

你正在试着使用正则表达式去匹配一大块的文本,而你需要跨越多行去匹配。

这个问题很典型的出现在当你用点 . 去匹配任意字符的时候,忘记了点 . 不能匹配换行符的事实。比如,假设你想试着去匹配 C 语言分割的注释(即 /* ... */ 之间的内容):

>>> comment = re.compile(r'/\*(.*?)\*/')
>>> text1 = '/* this is a comment */'
>>> text2 = '''/* this is a
... multiline comment */
... '''
>>>
>>> comment.findall(text1)
[' this is a comment ']
>>> comment.findall(text2)
[]
  • /\* → 匹配注释的开始符号 /*
    • / 匹配字面量 /
    • \* 匹配字面量 ** 在正则中有特殊含义,所以需要转义 \*)。
  • (.*?) → 非贪婪匹配:
    • . 匹配任意字符(除换行符 \n,除非使用 re.DOTALL 标志)。
    • *? 表示 前一个字符 . 可以出现 0 0 0 次或多次,但尽可能少匹配(非贪婪模式)。
    • ( ) 表示捕获分组,方便提取注释内容。
  • \*/ → 匹配注释的结束符号 */
    • \* 匹配字面量 *
    • / 匹配字面量 /

为了修正这个问题,你可以修改模式字符串,增加对换行的支持。比如:

>>> comment = re.compile(r'/\*((?:.|\n)*?)\*/')
>>> comment.findall(text2)
[' this is a\n multiline comment ']

在这个模式中, (?:.|\n) 指定了一个非捕获组(也就是它定义了一个仅仅用来做匹配,而不能通过单独捕获或者编号的组)。

  • (?:...) 是一个非捕获分组(仅用于分组,不捕获匹配内容)。
  • .|\n 匹配任意字符 . 或换行符 \n,确保可以跨行匹配。

re.compile() 函数接受一个标志参数叫 re.DOTALL,在这里非常有用。它可以让正则表达式中的点 . 匹配包括换行符在内的任意字符。比如:

>>> comment = re.compile(r'/\*(.*?)\*/', re.DOTALL)
>>> comment.findall(text2)
[' this is a\n multiline comment ']

对于简单的情况使用 re.DOTALL 标记参数工作的很好,但是如果模式非常复杂或者是为了构造字符串令牌而将多个模式合并起来, 这时候使用这个标记参数就可能出现一些问题。如果让你选择的话,最好还是定义自己的正则表达式模式,这样它可以在不需要额外的标记参数下也能工作的很好。

9.将 Unicode 文本标准化

你正在处理 Unicode 字符串,需要确保所有字符串在底层有相同的表示。

在 Unicode 中,某些字符能够用多个合法的编码表示。为了说明,考虑下面的这个例子:

>>> s1 = 'Spicy Jalape\u00f1o'
>>> s2 = 'Spicy Jalapen\u0303o'
>>> s1
'Spicy Jalapeño'
>>> s2
'Spicy Jalapeño'
>>> s1 == s2
False
>>> len(s1)
14
>>> len(s2)
15

这里的文本 Spicy Jalapeño 使用了两种形式来表示。 第一种使用整体字符 ñU+00F1),第二种使用拉丁字母 n 后面跟一个 ~ 的组合字符(U+0303)。

在需要比较字符串的程序中使用字符的多种表示会产生问题。为了修正这个问题,你可以使用 unicodedata 模块先将文本标准化:

>>> import unicodedata
>>> t1 = unicodedata.normalize('NFC', s1)
>>> t2 = unicodedata.normalize('NFC', s2)
>>> t1 == t2
True
>>> print(ascii(t1))
'Spicy Jalape\xf1o'
>>> t3 = unicodedata.normalize('NFD', s1)
>>> t4 = unicodedata.normalize('NFD', s2)
>>> t3 == t4
True
>>> print(ascii(t3))
'Spicy Jalapen\u0303o'

normalize() 第一个参数指定字符串标准化的方式。

  • NFC 表示字符应该是整体组成(比如可能的话就使用单一编码)。
  • NFD 表示字符应该分解为多个组合字符表示。

Python 同样支持扩展的标准化形式 NFKCNFKD,它们在处理某些字符的时候增加了额外的兼容特性。比如:

>>> s = '\ufb01' # A single character
>>> s
'fi'
>>> unicodedata.normalize('NFD', s)
'fi'
# Notice how the combined letters are broken apart here
>>> unicodedata.normalize('NFKD', s)
'fi'
>>> unicodedata.normalize('NFKC', s)
'fi'

标准化对于任何需要以一致的方式处理 Unicode 文本的程序都是非常重要的。当处理来自用户输入的字符串而你很难去控制编码的时候尤其如此。

在清理和过滤文本的时候字符的标准化也是很重要的。比如,假设你想清除掉一些文本上面的变音符的时候(可能是为了搜索和匹配):

>>> t1 = unicodedata.normalize('NFD', s1)
>>> ''.join(c for c in t1 if not unicodedata.combining(c))
'Spicy Jalapeno'

最后一个例子展示了 unicodedata 模块的另一个重要方面,也就是测试字符类的工具函数。combining() 函数可以测试一个字符是否为和音字符。 在这个模块中还有其他函数用于查找字符类别,测试是否为数字字符等等。

10.在正则式中使用 Unicode

你正在使用正则表达式处理文本,但是关注的是 Unicode 字符处理。

默认情况下 re 模块已经对一些 Unicode 字符类有了基本的支持。比如,\\d 已经匹配任意的 Unicode 数字字符了:

>>> import re
>>> num = re.compile('\d+')
>>> # ASCII digits
>>> num.match('123')
<_sre.SRE_Match object at 0x1007d9ed0>
>>> # Arabic digits
>>> num.match('\u0661\u0662\u0663')
<_sre.SRE_Match object at 0x101234030>

如果你想在模式中包含指定的 Unicode 字符,你可以使用 Unicode 字符对应的转义序列(比如 \uFFF 或者 \UFFFFFFF)。比如,下面是一个匹配几个不同阿拉伯编码页面中所有字符的正则表达式:

>>> arabic = re.compile('[\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff]+')

当执行匹配和搜索操作的时候,最好是先标准化并且清理所有文本为标准化格式。但是同样也应该注意一些特殊情况,比如在忽略大小写匹配和大小写转换时的行为。

>>> pat = re.compile('stra\u00dfe', re.IGNORECASE) # \u00df 是 'ß' 的 Unicode 编码
>>> s = 'straße' # 德语单词 "Straße"(街道)的小写形式
>>> pat.match(s) # Matches
<_sre.SRE_Match object at 0x10069d370>
>>> pat.match(s.upper()) # Doesn't match
>>> s.upper() # Case folds
'STRASSE'
  • re.IGNORECASEUnicode 字符
    • re.IGNORECASE 使正则表达式 不区分大小写,例如 A 可以匹配 a
    • ß 是一个特殊情况:
      • 它的 大写形式是 SS(根据德语正字法规则)。
      • 因此,'straße'.upper() 会变成 'STRASSE',而不仅仅是简单的单个字符大小写转换。
  • 为什么 pat.match(s) 成功,但 pat.match(s.upper()) 失败?
    • pat.match(s)
      • 正则表达式模式是 stra\u00dfe(即 straße),且 re.IGNORECASE 启用。
      • 直接匹配 s = 'straße' 时,完全一致(不区分大小写),所以匹配成功。
    • pat.match(s.upper())
      • s.upper() 返回 'STRASSE'(因为 ßSS)。
      • 但正则表达式模式是 straße,它的大写形式应该是 STRASSE,而模式本身并未包含 SS
      • 因此,正则引擎无法将 SSß 视为等效(尽管人类知道它们是同一字母的不同形式),导致匹配失败。
  • 根本原因:
    • re.IGNORECASE 在 Python 的 re 模块中默认不处理 Unicode 的特殊大小写映射(如 ßSS)。
    • 如果要正确处理 Unicode 大小写折叠(case folding),需要使用 re.UNICODE 标志(但在 Python 中,re.IGNORECASE 已隐含 Unicode 支持,但对 ß 仍无效)。
    • 更彻底的解决方案是使用 casefold() 方法。

混合使用 Unicode 和正则表达式通常会让你抓狂。如果你真的打算这样做的话,最好考虑下安装第三方正则式库,它们会为 Unicode 的大小写转换和其他大量有趣特性提供全面的支持,包括模糊匹配。

相关文章:

【Python Cookbook】字符串和文本(二)

字符串和文本&#xff08;二&#xff09; 6.字符串忽略大小写的搜索替换7.最短匹配模式8.多行匹配模式9.将 Unicode 文本标准化10.在正则式中使用 Unicode 6.字符串忽略大小写的搜索替换 你需要以忽略大小写的方式搜索与替换文本字符串。 为了在文本操作时忽略大小写&#xf…...

Redisson 实现分布式锁简单解析

目录 Redisson 实现分布式锁业务方法&#xff1a;加锁逻辑LockUtil 工具类锁余额方法&#xff1a;工具类代码枚举代码 RedisUtil 工具类tryLock 方法及重载【分布式锁具体实现】Supplier 函数式接口调用分析 Redisson 实现分布式锁 业务方法&#xff1a; 如图&#xff0c;简单…...

六十天Linux从0到项目搭建(第五天)(file、bash 和 shell 的区别、目录权限、默认权限umask、粘滞位、使用系统自带的包管理工具)

1. file [选项] 文件名 用于确定文件类型的实用工具。它会通过分析文件内容&#xff08;而不仅仅是文件扩展名&#xff09;来判断文件的实际类型 示例输出解析 $ file /bin/bash /bin/bash: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, i…...

信源的分类及数学模型

信源的分类及数学模型 按照信源发出的时间和消息分布分为离散信源和连续信源 按照信源发出符号之间的关系分为无记忆信源和有记忆信源 单符号离散信源&#xff08;一维离散信源&#xff09; 信源输出的消息数有限或可数&#xff0c;且每次只输出符号集的一个消息 样本空间&…...

嵌入式硬件工程师从小白到入门-PCB绘制(二)

PCB绘制从小白到入门&#xff1a;知识点速通与面试指南 一、PCB设计核心流程 需求分析 明确电路功能&#xff08;如电源、信号处理、通信&#xff09;。确定关键参数&#xff08;电压、电流、频率、接口类型&#xff09;。 原理图设计 元器件选型&#xff1a;匹配封装、电压、…...

抽象工厂设计模式及应用案例

引言 在软件开发中&#xff0c;合理的设计模式可以有效地提高代码的可维护性、可扩展性和可重用性。抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;便是一个重要的创建型设计模式&#xff0c;它允许我们在不指定具体类的情况下&#xff0c;创建一系列相关或相…...

LVS NAT模式实现三台RS的轮询访问

节点规划: 配置RS&#xff1a; RS1-RS3的网关配置均为 192.168.163.8 配置RS1&#xff1a; [rootlocalhost ~]# hostnamectl hostname rs1 [rootlocalhost ~]# nmcli c modify ens160 ipv4.method manual ipv4.addresses 192.168.163.7/24 ipv4.gateway 192.168.163.8 conne…...

LSM-Tree(Log-Structured Merge-Tree)详解

1. 什么是 LSM-Tree? LSM-Tree(Log-Structured Merge-Tree)是一种 针对写优化的存储结构,广泛用于 NoSQL 数据库(如 LevelDB、RocksDB、HBase、Cassandra)等系统。 它的核心思想是: 写入时只追加写(Append-Only),将数据先写入内存缓冲区(MemTable)。内存数据满后…...

uni-app jyf-parser将字符串转化为html 和 rich-text

uni-app jyf-parser将字符串转化为html-CSDN博客 方法二&#xff1a; rich-text | uni-app...

Docker+Ollama+Xinference+RAGFlow+Dify部署及踩坑问题

目录 一、Xinference部署 &#xff08;一&#xff09;简介 &#xff08;二&#xff09;部署 &#xff08;三&#xff09;参数 &#xff08;四&#xff09;错误问题 &#xff08;五&#xff09;Xinference配置Text-embedding模型 &#xff08;六&#xff09;Xinference配…...

CentOS 7 搭建基于匿名用户的 FTP 服务

1. 安装 VSFTPD yum install vsftpd -y 2. 配置 VSFTPD 编辑主配置文 vi /etc/vsftpd/vsftpd.conf 以下配置项存在或修改为对应值 anonymous_enableYES # 启用匿名用户 local_enableNO # 禁用本地用户 write_enableYES # 允许写入&#xff08;若需要匿名上传&#xff0…...

【机器学习】什么是线性回归?

什么是线性回归&#xff1f; 线性回归是一种 监督学习算法&#xff0c;它通过拟合一个直线&#xff08;或平面&#xff0c;高维空间下是超平面&#xff09;来建立 输入特征 和 输出目标 之间的关系。简单来说&#xff0c;线性回归就是找出一个数学方程&#xff08;通常是线性方…...

零、ubuntu20.04 安装 anaconda

1.anaconda下载 地址&#xff1a;Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 选择&#xff1a;Anaconda3-2023.07-2-Linux-x86_64.sh 2.anaconda安装 选择下载目录&#xff0c;选在在终端中打开&#xff0c;然后在终端输入安装命…...

Web纯前端实现在线打开编辑保存PPT幻灯片

很多项目中有时会需要在线打开PPT并编辑保存到服务器。猿大师办公助手可以完美调用本地office在线打开ppt文件&#xff0c;跟本地打开效果一样。还可以在线打开word、excel、pdf等文件&#xff0c;支持本机OFFICE完整嵌入模式&#xff0c;本机OFFICE所有功能基本都可以在网页上…...

LeetCode热题100精讲——Top3:最长连续序列【哈希】

你好&#xff0c;我是安然无虞。 文章目录 题目背景最长连续序列C解法Python解法 题目背景 如果大家对于 哈希 类型的概念并不熟悉, 可以先看我之前为此专门写的算法详解: 蓝桥杯算法竞赛系列第九章巧解哈希题&#xff0c;用这3种数据类型足矣 最长连续序列 题目链接&#x…...

vue2相关 基础命令

vue2 基础命令 vue简介&#xff0c;Vue 2 已于 2023 年 12 月 31 日停止维护。详见 Vue 2 终止支持 (EOL)。 安装完 Visual Studio Code后&#xff0c;打开项目目录&#xff0c; 在目录位置输入cmd&#xff0c;然后在命令行输入 code . 就可以在VScode打开项目。 公司的前后端…...

2025年渗透测试面试题总结-某 长亭(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 长亭 一、Spring SpEL表达式注入漏洞 1. 技术原理 2. 利用条件 3. 攻击方法 4. 防御策略 二、Jav…...

【web3】

检测钱包是否安装 方法一 // npm install metamask/detect-provider import detectEthereumProvider from metamask/detect-provider// 检测钱包是否安装 const isProvider await detectEthereumProvider() if(!isProvider) {proxy.$modal.msgError("请安装钱包")…...

Ubuntu部署Dufs文件服务器

安装dufs 安装cargo apt install cargo升级rust工具链 apt install rustup rustup update stable查看rust版本&#xff0c;需要>1.81 rustc --version安装dufs cargo install dufs将dufs加入环境变量 sudo vim ~/.bashrc export PATH"$HOME/.cargo/bin:$PATH" sou…...

速卖通API数据清洗实战:从原始JSON到结构化商品数据库

下面将详细介绍如何把速卖通 API 返回的原始 JSON 数据清洗并转换为结构化商品数据库。 1. 数据获取 首先要借助速卖通 API 获取商品数据&#xff0c;以 Python 为例&#xff0c;可使用requests库发送请求并得到 JSON 数据。 import requests# 替换为你的 API Key 和 Secret …...

前端模拟 websocket 请求小工具

背景&#xff1a; 后端写好websocket 接口后&#xff0c;用postman的某些版本无法直接模拟websocket请求&#xff0c;所以想着自制一个小工具。 使用方法&#xff1a; 直接复制以下代码到文本文件中&#xff0c;修改服务端端地址&#xff0c;保存为 .html的后缀名&#xff0c;…...

docker安装hyperf环境,连接本机redis问题处理

错误信息显示“Connection refused”&#xff0c;这通常说明 Docker 容器内的 Hyperf 项目无法连接到你本机的 Redis 服务。 1. 容器内的 127.0.0.1 指向问题 在 Docker 容器中&#xff0c;127.0.0.1 指的是容器本身&#xff0c;而不是宿主机&#xff08;你的 Mac&#xff09;…...

【极速版 -- 大模型入门到进阶】快速了解大型语言模型

文章目录 &#x1f30a; 大模型作为一种生成式人工智慧&#xff0c;厉害在哪儿&#xff1f;-> 通用能力&#x1f30a; LLM 如何生成输出&#xff1a;简而言之就是文字接龙&#x1f30a; GPT 之前 ...&#xff1a;模型规模和数据规模概览&#x1f30a; ChatGPT 有三个训练阶段…...

精选10个好用的WordPress免费主题

10个好用的WordPress免费主题 1. Astra Astra 是全球最受欢迎的 WordPress 主题。它功能丰富&#xff0c;易于使用&#xff0c;SEO友好&#xff0c;是第一个安装量突破100万的非默认主题&#xff0c;并获得了5000多个五星好评。 它完美集成了Elementor、Beaver&#xff0c;古…...

算法日常刷题笔记(6)

重整旗鼓 第六篇笔记 第一天 使字符串平衡的最小交换次数 给你一个字符串 s &#xff0c;下标从 0 开始 &#xff0c;且长度为偶数 n 。字符串 恰好 由 n / 2 个开括号 [ 和 n / 2 个闭括号 ] 组成。 只有能满足下述所有条件的字符串才能称为 平衡字符串 &#xff1a; 字符串…...

【Unity3D脚本与系统设计6】鼠标触摸超时待机实现

实现步骤 在Unity中实现一个功能&#xff0c;当鼠标或触摸超过一定时间没有操作时&#xff0c;自动返回待机界面。 检测输入 首先&#xff0c;我需要检测用户的输入&#xff0c;无论是鼠标还是触摸。Unity的Input系统可以检测到鼠标和触摸事件&#xff0c;比如Input.GetAxis…...

【Spring篇】Spring的生命周期

一、Bean 生命周期的核心阶段 1. 实例化&#xff08;Instantiation&#xff09; • 触发时机&#xff1a;容器启动时&#xff08;单例 Bean&#xff09;或请求时&#xff08;原型 Bean&#xff09;。 • 实现方式&#xff1a; 通过反射&#xff08;Class.newInstance() 或构造…...

C# 的Lambda表达式‌常见用法和示例

C# 的 ‌Lambda 表达式‌是一种强大的语法糖&#xff0c;能够极大简化代码并增强灵活性。以下是它的主要功能和应用场景&#xff0c;结合具体示例说明&#xff1a; 1. ‌简化委托实例化‌ Lambda 可以快速定义委托&#xff08;如 Func、Action&#xff09;&#xff0c;无需显式…...

C++学习之路:从头搞懂配置VScode开发环境的逻辑与步骤

目录 编辑器与IDE基于vscode的C开发环境配置1. 下载vscode、浅尝编译。番外篇 2. 安装插件&#xff0c;赋能编程。3. 各种json文件的作用。c_cpp_properties.jsontask.jsonlaunch.json 总结&&彩蛋 编辑器与IDE 上一篇博客已经介绍过了C程序的一个编译流程&#xff0c;从…...

Web3与网络安全:如何确保去中心化应用的安全性

Web3与网络安全&#xff1a;如何确保去中心化应用的安全性 随着区块链技术的蓬勃发展&#xff0c;Web3的概念逐渐成为互联网发展的新趋势。Web3强调去中心化、用户主权和数据隐私&#xff0c;它的核心是构建一个更加开放、透明和安全的网络环境。然而&#xff0c;随着去中心化…...