18. 正则表达式(Regular Expression / Regex)¶
一句话说明:正则表达式是一套"文本搜索模式语言",让你用一行代码就能从海量文本中精准找到、提取、替换你想要的内容——生信分析中处理序列文件、注释文件、日志文件的必备技能。
一、核心概念(白话版)¶
1.1 正则是什么?¶
正则表达式(Regular Expression,简称 regex 或 regexp)就是一个描述文本模式的"模板"。
白话比方:想象你在一本电话簿里找电话号码。你不知道具体号码是多少,但你知道"电话号码长这样:3位区号-8位数字"。正则表达式就是帮你把这个"长这样"的规则写成代码能懂的语言。
模板:\d{3}-\d{8}
意思:3个数字 + 一个横杠 + 8个数字
能匹配:010-12345678、021-87654321
不能匹配:abc-12345678、01-1234
1.2 为什么生信要学正则?¶
| 场景 | 不用正则 | 用正则 |
|---|---|---|
| 从 FASTA 文件提取基因名 | 一行行肉眼看,或写很多 if 判断 | 一行代码搞定 |
| 批量改文件名 | 手动一个个改 | sed 一行搞定 |
| 从 GFF 文件提取 gene_id | 写很多 split + 循环 | grep -oP 一行搞定 |
| 过滤质量低的序列ID | 写复杂的字符串判断 | 正则匹配模式一步到位 |
总结:正则 = 文本处理的瑞士军刀,生信离不开它。
1.3 白话总结¶
- 正则 = 一套描述"文本长什么样"的规则语言
- 匹配 = 看某段文字符不符合这个规则
- 捕获 = 从符合规则的文字中,抠出你要的那部分
- 替换 = 把符合规则的文字换成别的
二、基础语法(每个都有白话解释 + 生信实例)¶
2.1 字符匹配¶
| 语法 | 白话解释 | 生信实例 | 匹配结果 |
|---|---|---|---|
. |
匹配任意一个字符(除了换行符)。就像扑克牌里的"万能牌" | A.G 匹配 DNA 中的 ATG、ACG、AAG |
ATG ✓ ACG ✓ AG ✗ |
\d |
匹配一个数字(0-9)。d = digit(数字) | sample_\d 匹配 sample_1、sample_9 |
sample_1 ✓ sample_a ✗ |
\w |
匹配一个"单词字符"(字母、数字、下划线)。w = word | gene_\w+ 匹配 gene_abc、gene_123 |
gene_abc ✓ gene-abc ✗ |
\s |
匹配一个空白字符(空格、Tab、换行)。s = space | ID\s+name 匹配 ID name(多个空格) |
ID name ✓ IDname ✗ |
[abc] |
匹配方括号里任意一个字符。就像"从 a、b、c 里选一个" | [ATCG] 匹配 DNA 碱基 |
A ✓ T ✓ X ✗ |
[^abc] |
匹配不在方括号里的任意字符。^ 在 [] 里表示"取反" |
[^ATCG] 匹配非标准碱基 |
N ✓ A ✗ |
补充常用字符类:
[A-Z] 匹配任意一个大写字母
[a-z] 匹配任意一个小写字母
[0-9] 等同于 \d,匹配一个数字
[A-Za-z] 匹配任意一个英文字母
[ATCG] 匹配 DNA 的四种碱基(生信专用)
[ACGU] 匹配 RNA 的四种碱基
2.2 量词(控制"出现几次")¶
| 语法 | 白话解释 | 生信实例 | 匹配结果 |
|---|---|---|---|
* |
前面的东西出现 0 次或多次。"有没有都行,有就尽量多" | AT*G → A后面跟0或多个T再跟G |
AG ✓ ATG ✓ ATTTG ✓ |
+ |
前面的东西出现 1 次或多次。"至少要有一个" | AT+G → A后面至少1个T再跟G |
ATG ✓ ATTTG ✓ AG ✗ |
? |
前面的东西出现 0 次或 1 次。"可有可无" | colou?r → u 可有可无 |
color ✓ colour ✓ |
{n} |
前面的东西恰好出现 n 次 | [ATCG]{3} → 恰好3个碱基(一个密码子) |
ATG ✓ AT ✗ ATCG ✗ |
{n,m} |
前面的东西出现 n 到 m 次 | [ATCG]{20,30} → 20-30个碱基的序列 |
(20-30 bp 的短序列) |
白话记忆口诀:
- * → "星星"可以没有(0次起步)
- + → "加号"至少加一个(1次起步)
- ? → "问号"就是在问"要不要"(0或1次)
2.3 定位(锚点——不匹配字符,匹配"位置")¶
| 语法 | 白话解释 | 生信实例 | 说明 |
|---|---|---|---|
^ |
匹配行的开头。"我要找的东西必须在最前面" | ^> 匹配 FASTA 文件中以 > 开头的行(头信息行) |
只匹配行首的 > |
$ |
匹配行的结尾。"我要找的东西必须在最后面" | \*$ 匹配蛋白质序列末尾的终止符 * |
只匹配行末的 * |
\b |
匹配单词边界。"一个单词的开头或结尾位置" | \bgene\b 只匹配独立的 gene,不匹配 genes 或 genetically |
精确匹配整个单词 |
注意区分:
- ^ 在 [] 外面 = 行开头
- ^ 在 [] 里面 = 取反(如 [^ATCG])
2.4 分组与或¶
| 语法 | 白话解释 | 生信实例 |
|---|---|---|
() |
分组:把多个字符当成一个整体,同时"记住"匹配到的内容(捕获) | (gene_id\|transcript_id) "([^"]+)" → 捕获 GFF 中的 ID 值 |
\| |
或:匹配左边或右边。就像"A 或者 B" | ATG\|GTG\|TTG → 匹配三种起始密码子 |
注意:表格中的
\|是 Markdown 表格语法需要的转义。在实际 Python/Shell 正则中直接写|即可,如ATG|GTG|TTG。
分组的两大用途:
1. 整体操作:(AT){3} 匹配 ATATAT(AT 重复3次)
2. 捕获提取:用 () 圈住你要的部分,之后可以单独取出来
2.5 转义¶
| 语法 | 白话解释 | 实例 |
|---|---|---|
\ |
让特殊字符变成普通字符。就像给特殊字符"取消超能力" | \. 匹配真正的句号(不是"任意字符") |
什么时候需要转义? 当你想匹配这些字符本身时:. * + ? ^ $ { } [ ] ( ) | \
想匹配文件名 "result.txt":
错误:result.txt → 这里的 . 会匹配任意字符,比如 "resultXtxt"
正确:result\.txt → \. 就是匹配真正的句号
三、生信常用正则模式(每个都有完整实例)¶
3.1 匹配 FASTA 头信息¶
FASTA 文件格式:每条序列以 > 开头的描述行,后面跟序列。
示例数据:
>gi|12345|ref|NM_001234.5| Homo sapiens gene ABC
ATGCGATCGATCG
>sp|P12345|BRCA1_HUMAN BRCA1 protein
MSDKEVTDPS
# 匹配 FASTA 头信息行
pattern = r'^>(.+)$'
# 解释:^ 行开头,> 就是大于号,(.+) 捕获后面所有内容,$ 行结尾
# 提取 NCBI gi 号
pattern = r'>gi\|(\d+)\|'
# 解释:\| 是转义的竖线(因为 | 在正则里是"或"),(\d+) 捕获数字
# 提取 UniProt ID(如 P12345)
pattern = r'>sp\|([A-Z0-9]+)\|'
3.2 提取基因 ID¶
# 从 GFF/GTF 属性字段提取 gene_id
# 示例行:gene_id "ENSG00000139618"; transcript_id "ENST00000357654";
pattern = r'gene_id "([^"]+)"'
# 解释:[^"]+ 匹配引号内的所有内容(除了引号本身)
# 提取 Ensembl 基因 ID(以 ENSG 开头,后跟11位数字)
pattern = r'(ENSG\d{11})'
# 解释:ENSG 后面跟恰好11个数字
# 提取 NCBI Gene ID(纯数字)
pattern = r'GeneID:(\d+)'
3.3 匹配 DNA / 蛋白质序列¶
# 纯 DNA 序列(只含 ATCGN)
pattern = r'^[ATCGNatcgn]+$'
# 解释:从头到尾都是碱基字符
# 匹配起始密码子
pattern = r'ATG'
# 匹配终止密码子
pattern = r'(TAA|TAG|TGA)'
# 匹配一个完整的开放阅读框(ORF)—— 简化版
pattern = r'ATG([ATCG]{3})*(TAA|TAG|TGA)'
# 解释:ATG 开始,中间是3的倍数个碱基,以终止密码子结束
# 蛋白质序列(20种氨基酸单字母)
pattern = r'^[ACDEFGHIKLMNPQRSTVWY]+$'
# 带终止符的蛋白质
pattern = r'^[ACDEFGHIKLMNPQRSTVWY]+\*?$'
3.4 解析 GFF/GTF 文件¶
GFF3 格式每行9列,Tab 分隔:
chr1 ensembl gene 11869 14409 . + . ID=ENSG00000223972;Name=DDX11L1
chr1 ensembl exon 11869 12227 . + . Parent=ENST00000456328
# 提取基因名
pattern = r'Name=([^;]+)'
# 解释:Name= 后面到分号之前的内容
# 提取坐标信息(第4、5列)
pattern = r'^\S+\t\S+\t(\S+)\t(\d+)\t(\d+)'
# 解释:\S+ 匹配非空白字符,\t 匹配 Tab
# 只提取 gene 类型的行
pattern = r'^\S+\t\S+\tgene\t'
# 从 GTF 提取 gene_id 和 gene_name
pattern = r'gene_id "([^"]+)".*gene_name "([^"]+)"'
3.5 从文件名提取样本编号¶
# 文件名格式:sample_01_R1.fastq.gz
pattern = r'sample_(\d+)_R([12])\.fastq\.gz'
# 解释:(\d+) 捕获样本号,([12]) 捕获 R1 或 R2
# 更复杂的文件名:T2D_patient03_gut_16S_R1.fq.gz
pattern = r'(\w+?)_patient(\d+)_(\w+)_(\w+)_R([12])\.fq\.gz'
# 捕获组:1=项目名,2=患者号,3=样本类型,4=测序方法,5=端号
# 批量提取样本 ID(只要数字部分)
pattern = r'[A-Za-z_]*(\d+)'
四、Python re 模块实操¶
4.1 四大核心函数¶
import re
# ===== 准备示例数据 =====
fasta_header = ">gi|12345|ref|NM_001234.5| Homo sapiens BRCA1"
dna_sequence = "ATGCGATCGATCGATCGTAA"
gtf_line = 'chr1\tensembl\tgene\t11869\t14409\t.\t+\t.\tgene_id "ENSG00000223972"; gene_name "DDX11L1";'
# ==========================================
# 1. re.search() —— 在字符串中搜索第一个匹配
# ==========================================
# 白话:从整个字符串中找到第一个符合条件的地方
result = re.search(r'gi\|(\d+)\|', fasta_header)
if result:
print(f"找到 GI 号: {result.group(1)}") # 输出: 找到 GI 号: 12345
print(f"完整匹配: {result.group(0)}") # 输出: 完整匹配: gi|12345|
print(f"匹配位置: {result.start()}-{result.end()}") # 输出位置
# ==========================================
# 2. re.match() —— 从字符串开头匹配
# ==========================================
# 白话:只在字符串的最开头找,不从中间找
# 注意:match 只匹配开头!这是它和 search 最大的区别
# 这个能匹配,因为 > 在开头(match 自动从开头匹配,^ 可省略但加上更清晰)
result = re.match(r'>(.+)', fasta_header)
if result:
print(f"头信息: {result.group(1)}")
# 这个不能匹配,因为 gi 不在开头(前面有 >)
result = re.match(r'gi', fasta_header)
print(result) # 输出: None
# ==========================================
# 3. re.findall() —— 找到所有匹配,返回列表
# ==========================================
# 白话:把所有符合条件的全部找出来,打包成一个列表
# 找 DNA 中所有的密码子(每3个碱基一组)
codons = re.findall(r'[ATCG]{3}', dna_sequence)
print(f"所有密码子: {codons}")
# 输出: ['ATG', 'CGA', 'TCG', 'ATC', 'GAT', 'CGT']
# 注意:最后剩 "AA" 不足3个字符,{3} 匹配不到,所以被丢弃
# findall 是从左到右非重叠匹配,每次吃掉3个字符再继续
# 从 GTF 行中提取所有引号内的值
values = re.findall(r'"([^"]+)"', gtf_line)
print(f"所有属性值: {values}")
# 输出: ['ENSG00000223972', 'DDX11L1']
# 多个捕获组时,返回元组列表
pairs = re.findall(r'(\w+_\w+) "([^"]+)"', gtf_line)
print(f"键值对: {pairs}")
# 输出: [('gene_id', 'ENSG00000223972'), ('gene_name', 'DDX11L1')]
# ==========================================
# 4. re.sub() —— 替换匹配到的内容
# ==========================================
# 白话:找到符合条件的文字,换成你想要的
# 把 DNA 序列中的 T 换成 U(DNA → RNA 转录)
rna = re.sub(r'T', 'U', dna_sequence)
print(f"RNA序列: {rna}")
# 输出: AUGCGAUCGAUCGAUCGUAA
# 清理 FASTA 头信息,只保留基因名
clean = re.sub(r'>gi\|\d+\|ref\|[\w.]+\|\s*', '', fasta_header)
print(f"基因名: {clean}")
# 输出: Homo sapiens BRCA1
# 标准化样本名(去掉多余的下划线和空格)
messy_name = "sample__01___R1"
clean_name = re.sub(r'_+', '_', messy_name) # 多个下划线变一个
print(f"清理后: {clean_name}")
# 输出: sample_01_R1
4.2 分组捕获详解¶
import re
# ===== 命名分组(推荐!代码更易读)=====
# 语法:(?P<名字>pattern)
gtf_line = 'gene_id "ENSG00000223972"; gene_name "DDX11L1"; transcript_id "ENST00000456328";'
# 使用命名分组提取
pattern = r'gene_id "(?P<gene_id>[^"]+)".*gene_name "(?P<gene_name>[^"]+)"'
match = re.search(pattern, gtf_line)
if match:
print(f"基因ID: {match.group('gene_id')}") # ENSG00000223972
print(f"基因名: {match.group('gene_name')}") # DDX11L1
print(f"所有捕获: {match.groupdict()}") # 返回字典
# {'gene_id': 'ENSG00000223972', 'gene_name': 'DDX11L1'}
# ===== 实战:批量解析 FASTA 文件 =====
fasta_content = """>sp|P12345|BRCA1_HUMAN BRCA1 protein
MSDKEVTDPS
>sp|Q67890|TP53_HUMAN Tumor protein p53
MEEPQSDPSVEPPLSQ"""
# 提取所有蛋白质的 UniProt ID 和蛋白名
pattern = r'>sp\|([A-Z0-9]+)\|(\w+)\s+(.+)'
results = re.findall(pattern, fasta_content)
for uniprot_id, protein_code, description in results:
print(f"UniProt: {uniprot_id}, 代码: {protein_code}, 描述: {description}")
# 输出:
# UniProt: P12345, 代码: BRCA1_HUMAN, 描述: BRCA1 protein
# UniProt: Q67890, 代码: TP53_HUMAN, 描述: Tumor protein p53
# ===== 实战:用 re.sub 和分组进行格式转换 =====
# 把 "姓 名" 格式转成 "名, 姓" 格式
authors = "Zhang Wei, Li Ming, Wang Fang"
# 每个名字是:一个大写字母开头的单词 + 空格 + 一个大写字母开头的单词
converted = re.sub(r'(\b[A-Z]\w+)\s+(\b[A-Z]\w+)', r'\2, \1', authors)
# \1 = 第一个分组(姓),\2 = 第二个分组(名)
print(f"转换后: {converted}")
# ===== re.compile() —— 预编译提高效率 =====
# 如果同一个正则要用很多次,先编译再用,速度更快
gene_pattern = re.compile(r'gene_id "([^"]+)"')
# 模拟逐行处理 GTF 文件
gtf_lines = [
'chr1\t.\tgene\t100\t200\t.\t+\t.\tgene_id "ENSG00000001"; gene_name "GENE_A";',
'chr1\t.\texon\t100\t150\t.\t+\t.\tgene_id "ENSG00000001"; exon_number "1";',
'chr2\t.\tgene\t300\t500\t.\t-\t.\tgene_id "ENSG00000002"; gene_name "GENE_B";',
]
gene_ids = set()
for line in gtf_lines:
match = gene_pattern.search(line)
if match:
gene_ids.add(match.group(1))
print(f"所有基因ID: {gene_ids}")
# 输出: {'ENSG00000001', 'ENSG00000002'}
五、Shell 中的正则(grep / sed / awk)¶
5.1 grep —— 搜索匹配的行¶
# -E 使用扩展正则(推荐,不用转义 +、? 等)
# -P 使用 Perl 正则(功能最强,支持 \d 等)
# -o 只输出匹配到的部分(不是整行)
# -i 忽略大小写
# -c 统计匹配的行数
# -n 显示行号
# -v 反转匹配(显示不匹配的行)
# 1. 从 FASTA 文件中提取所有头信息行
grep '^>' sequences.fasta
# 2. 统计 FASTA 中有多少条序列
grep -c '^>' sequences.fasta
# 3. 提取所有基因 ID(Ensembl 格式)
grep -oP 'ENSG\d{11}' annotation.gtf
# 4. 找到包含特定基因名的行
grep -i 'brca1' annotation.gff
# 5. 过滤掉注释行(以 # 开头的行)
grep -v '^#' data.gff > clean_data.gff
# 6. 找 DNA 序列中的起始密码子位置
grep -n 'ATG' sequence.txt
# 7. 同时搜索多个模式
grep -E 'gene|exon|CDS' annotation.gff
# 8. 提取 fastq 文件中的序列 ID
# 注意:@ 不是正则特殊字符,不需要转义
grep -P '^@\S+' reads.fastq | head
5.2 sed —— 流编辑器(搜索替换)¶
# 基本语法:sed 's/查找/替换/标志' 文件
# 1. 把 FASTA 头信息中的空格替换成下划线
sed 's/ /_/g' sequences.fasta
# g = global,替换每行中所有匹配(不加 g 只替换第一个)
# 2. 删除空行
sed '/^$/d' file.txt
# d = delete,删除匹配到的行
# 3. 提取 FASTA 序列(去掉头信息行)
sed '/^>/d' sequences.fasta
# 4. 只保留头信息行并清理
sed -n '/^>/p' sequences.fasta
# -n = 安静模式(默认不输出),p = 打印匹配的行
# 5. 批量修改文件中的基因名
sed 's/gene_old/gene_new/g' annotation.gff > updated.gff
# 6. 删除行首行尾空白
sed 's/^[[:space:]]*//;s/[[:space:]]*$//' file.txt
# 7. 在 FASTA 头信息行后添加来源信息
sed 's/^>\(.*\)/>\1 [source=metagenome]/' sequences.fasta
5.3 awk —— 文本处理"小型编程语言"¶
# awk 默认以空白分隔字段,$1 = 第一列,$2 = 第二列 ...
# $0 = 整行,NR = 行号,NF = 当前行的字段数
# 1. 提取 GFF 文件中所有基因的染色体和坐标
awk '$3 == "gene" {print $1, $4, $5}' annotation.gff
# $3 是第3列(特征类型),只处理值为 "gene" 的行
# 2. 计算 GFF 中每个基因的长度
awk '$3 == "gene" {print $5 - $4 + 1}' annotation.gff
# 3. 提取 TSV 文件中特定列(用 Tab 分隔)
awk -F'\t' '{print $1, $9}' annotation.gff
# -F'\t' 指定 Tab 为分隔符
# 4. 用正则过滤:只打印基因 ID 匹配 ENSG 的行
awk '/ENSG[0-9]+/' annotation.gtf
# 5. 统计 FASTA 文件中序列总长度
awk '!/^>/ {total += length($0)} END {print "Total bases:", total}' sequences.fasta
# 6. 从 BLAST 输出中过滤 e-value < 1e-10 的结果
awk '$11 < 1e-10 {print $1, $2, $11}' blast_results.txt
六、常用速查表¶
6.1 元字符速查¶
| 字符 | 含义 | 记忆 |
|---|---|---|
. |
任意一个字符 | 点 = "随便什么都行" |
\d |
数字 [0-9] | d = digit |
\D |
非数字 | 大写 = 取反 |
\w |
字母/数字/下划线 | w = word |
\W |
非单词字符 | 大写 = 取反 |
\s |
空白字符 | s = space |
\S |
非空白字符 | 大写 = 取反 |
\t |
Tab 制表符 | t = tab |
\n |
换行符 | n = newline |
6.2 量词速查¶
| 量词 | 含义 | 等价写法 |
|---|---|---|
* |
0 次或多次 | {0,} |
+ |
1 次或多次 | {1,} |
? |
0 次或 1 次 | {0,1} |
{n} |
恰好 n 次 | - |
{n,} |
至少 n 次 | - |
{n,m} |
n 到 m 次 | - |
*? |
0 次或多次(懒惰模式) | 尽量少匹配 |
+? |
1 次或多次(懒惰模式) | 尽量少匹配 |
6.3 生信高频正则速查¶
| 用途 | 正则表达式 | 说明 |
|---|---|---|
| FASTA 头信息 | ^>(.+) |
以 > 开头的行 |
| Ensembl 基因 ID | ENSG\d{11} |
ENSG + 11 位数字 |
| Ensembl 转录本 ID | ENST\d{11} |
ENST + 11 位数字 |
| UniProt ID | [A-Z][0-9][A-Z0-9]{3}[0-9] |
如 P12345 |
| DNA 序列 | ^[ATCGNatcgn]+$ |
只含碱基字母 |
| 蛋白质序列 | ^[ACDEFGHIKLMNPQRSTVWY]+$ |
20 种氨基酸 |
| 起始密码子 | ATG |
甲硫氨酸 |
| 终止密码子 | (TAA\|TAG\|TGA) |
三种终止密码子(Python 中写 (TAA|TAG|TGA),此处 \| 是 Markdown 表格转义) |
| GTF gene_id | gene_id "([^"]+)" |
引号内的基因 ID |
| 样本编号 | sample_(\d+) |
下划线后的数字 |
| FASTQ 文件名 | (.+)_R([12])\.f(ast)?q(\.gz)?$ |
支持 .fq/.fastq/.gz |
| NCBI GI 号 | gi\|\d+\| |
管道符分隔 |
| 染色体编号 | chr([0-9XYM]+) |
chr1-22, X, Y, M |
| E-value | [0-9]+\.?[0-9]*[eE][-+]?[0-9]+ |
科学计数法 |
七、面试怎么答¶
Q1:什么是正则表达式?在生信中有什么用?¶
参考答案:
正则表达式是一种描述文本模式的语言,用于在字符串中进行搜索、匹配、提取和替换。在生信中非常常用: 1. 解析 FASTA/FASTQ 头信息,提取序列 ID 2. 从 GFF/GTF 注释文件中提取 gene_id、gene_name 等属性 3. 验证 DNA/蛋白质序列格式是否合法 4. 批量处理文件名,提取样本编号 5. 配合 grep/sed/awk 做文本过滤和格式转换
常用工具:Python 的
re模块,Shell 的grep -P、sed、awk。
Q2:re.search() 和 re.match() 有什么区别?¶
参考答案:
re.match()只从字符串开头匹配,如果开头不符合就返回 Nonere.search()扫描整个字符串,返回第一个匹配的位置例子:对于字符串
"hello world"-re.match(r'world', "hello world")→ None(开头不是 world) -re.search(r'world', "hello world")→ 匹配到 world实际工作中:90% 的情况用
re.search(),因为我们通常不确定目标在开头还是中间。只有明确知道模式在字符串开头时才用re.match()。
Q3:贪婪匹配和懒惰匹配有什么区别?请举例说明。¶
参考答案:
- 贪婪(Greedy):默认行为,尽可能多地匹配字符
- 懒惰(Lazy):加
?后缀,尽可能少地匹配字符例子:对于字符串
gene_id "ENSG001"; gene_name "TP53";- 贪婪:"(.+)"→ 匹配"ENSG001"; gene_name "TP53"(一直匹配到最后一个引号) - 懒惰:"(.+?)"→ 匹配"ENSG001"(匹配到第一个闭合引号就停)生信中经常需要用懒惰匹配或者用
[^"]+来精确提取引号内的内容。
Q4:如何用正则从 GTF 文件中提取所有基因 ID?¶
参考答案:
Shell 方法:
grep -oP 'gene_id "\K[^"]+' annotation.gtf | sort -u\K是 Perl 正则中的"重置匹配起点",只输出后面的内容。Python 方法:
import re with open('annotation.gtf') as f: gene_ids = set() for line in f: match = re.search(r'gene_id "([^"]+)"', line) if match: gene_ids.add(match.group(1))核心正则:
gene_id "([^"]+)",其中[^"]+匹配引号内的所有非引号字符。
Q5:*、+、? 三个量词有什么区别?¶
参考答案:
量词 含义 最少匹配次数 最多匹配次数 *零次或多次 0 无限 +一次或多次 1 无限 ?零次或一次 0 1 例子:对于字符串
"ATTTG"-AT*G→ 匹配ATTTG(T 出现 0 或多次,贪婪匹配全部3个T) -AT+G→ 匹配ATTTG(T 至少出现 1 次) -AT?G→ 不匹配(T 最多1次,但 A 和 G 之间有3个T,拼不成AG或ATG)
AT?G只能匹配AG或ATG这两种形式。
Q6:如何验证一个字符串是否是合法的 DNA 序列?¶
参考答案:
import re def is_valid_dna(seq): """检查是否为合法DNA序列(只含ATCGN)""" return bool(re.fullmatch(r'[ATCGNatcgn]+', seq)) print(is_valid_dna("ATCGATCG")) # True print(is_valid_dna("ATCGXYZ")) # False print(is_valid_dna("")) # False(+ 至少要1个)关键点:使用
re.fullmatch()或^...$确保整个字符串都符合,而不只是部分匹配。
Q7:如何用正则从文件名中提取样本信息?¶
参考答案:
import re filename = "T2D_patient03_gut_16S_R1.fq.gz" pattern = r'(?P<project>\w+?)_patient(?P<id>\d+)_(?P<tissue>\w+)_(?P<method>\w+)_R(?P<read>[12])\.fq\.gz' match = re.search(pattern, filename) if match: info = match.groupdict() # {'project': 'T2D', 'id': '03', 'tissue': 'gut', 'method': '16S', 'read': '1'}使用命名分组
(?P<name>...)让代码更可读。
八、延伸阅读¶
在线工具(练习推荐)¶
- regex101.com —— 最好的正则在线测试工具,支持实时高亮、解释每一部分
- regexr.com —— 另一个好用的可视化正则测试工具
推荐学习资源¶
- Python 官方文档:
re模块 → https://docs.python.org/3/library/re.html - Regular-Expressions.info → https://www.regular-expressions.info/ (最全面的正则教程网站)
- Python 正则表达式 HOWTO → https://docs.python.org/3/howto/regex.html (Python 官方教程)
进阶主题(面试加分项)¶
- 前瞻/后顾断言(Lookahead/Lookbehind):
(?=...)、(?<=...) - 非捕获组:
(?:...)—— 分组但不记住内容,性能更好 - re.compile() 预编译:处理大文件时提高效率
- re.MULTILINE 和 re.DOTALL 标志:处理多行文本
- Python
regex第三方库:比标准re功能更强(支持模糊匹配等)
九、本文速记卡片¶
正则表达式 = 文本模式的"模板语言"
最常用5个:
. 任意字符
\d 数字
+ 至少1个
* 0或多个
() 分组捕获
生信必记3个正则:
^>(.+) → FASTA 头信息
gene_id "([^"]+)" → GTF 基因 ID
[ATCG]+ → DNA 序列
Python 必记3个函数:
re.search() → 搜索(最常用)
re.findall() → 全部找出
re.sub() → 替换
Shell 必记3个命令:
grep -oP '正则' 文件 → 提取
sed 's/找/替/g' 文件 → 替换
awk '/正则/ {动作}' 文件 → 过滤+处理