4. 芳香性与特殊结构:芳香环表示、离子化合物、同位素标记
SMILES 编码遇到芳香环和特殊结构时,很多人会栽跟头。我早期处理药物分子数据库的时候,就因为这几种情况反复改过解析脚本。今天咱们把这三块硬骨头一次性啃干净。
4.1 芳香环的表示:小写 vs 大写
说白了,芳香环在 SMILES 里就两种写法:用小写字母表示芳香性,用大写字母表示单双键交替。给我记住一个核心原则:小写字母 = 芳香性。
举个最简单的例子——苯环:
# 芳香性表示(小写)
c1ccccc1
# Kekulé 式表示(大写)
C1=CC=CC=C1
你想想看,这两者差在哪儿?第一行是 RDKit 默认会解析成芳香性结构的写法。第二行是经典凯库勒结构,系统知道这是苯,但不一定自动识别成芳香性。
我在项目中遇到过这样的情况:用不同软件生成的 SMILES 混在一起,有些是 c1ccccc1,有些是 C1=CC=CC=C1。如果不统一处理,指纹计算的结果对不上。我的建议是统一化成小写芳香式再往下走。
Chem.MolFromSmiles() 自动识别芳香性,或者用 Chem.Kekulize() 手动转换。
4.2 杂环芳香性:常见模式
不只苯环,杂环也一样用大小写区分。吡啶、呋喃、噻吩、吡咯——这些环里的杂原子同样是小写:
# 吡啶 - 氮用小写 n
c1ccncc1
# 呋喃 - 氧用小写 o
c1ccoc1
# 噻吩 - 硫用小写 s
c1ccsc1
# 吡咯 - 氮用小写 n,但注意氢原子要显式写出 [nH]
c1cc[nH]c1
注意最后那个 [nH] 写法。吡咯环的氮上连着一个氢,RDKit 要求你显式写出来,不然解析会出错。我以前写自动化脚本时漏过这个,结果整批分子都没读进来,排查了半天才发现。
4.3 离子化合物与电荷表示
离子化合物的 SMILES 很简单:用方括号把原子或基团括起来,然后加正负号。钠离子、氯离子、铵根离子——都这路子:
# 单价离子
[Na+]
[Cl-]
[NH4+]
# 多价离子
[Ca+2]
[O-2]
# 两性离子(如氨基酸)
[N+](C)(C)C
[C-](=O)O
我这里要特别提醒一下电荷的位置。正负号 必须紧跟在原子符号或数字后面,中间不能有空格。另外,多价电荷可以用 +2 或 ++ 两种写法,都可以。我个人习惯用 [Ca+2],更直接。
[Zn+2] 时,不小心写成 [Zn++],RDKit 报错了,但有些老版本工具居然能读。为了兼容性,我建议用数字形式 [Zn+2]。
4.4 同位素标记表示
同位素标记在药物代谢研究里非常常见。你写 SMILES 时,直接把质量数写在元素符号前面,方括号包着:
# 碳-13 标记
[13C]
# 氘代(氘-2)
[2H] # 或者用 [D]
# 氚代(氚-3)
[3H] # 或者用 [T]
# 复杂分子的部分标记:C6H5-[13C](=O)OH
c1ccccc1[13C](=O)O
为什么会有 [D] 和 [T] 这种别名?历史遗留习惯。我个人不用它们,一是 D 容易和氨基酸符号混淆,二是在某些解析器里 [D] 可能被当成别的元素。老老实实用 [2H] 和 [3H] 最保险。
4.5 三种特殊结构的对比与处理
把这三类放在一起看更清楚:
| 类型 | 识别特征 | 典型表示 | 解析时的注意点 |
|---|---|---|---|
| 芳香性 | 小写字母 | c1ccccc1 |
Kekulize 前需要设置芳香性 |
| 离子电荷 | 方括号 + 正负号 | [Na+], [Ca+2] |
多价用数字,避免歧义 |
| 同位素标记 | 方括号 + 质量数 | [13C], [2H] |
禁用 [D]/[T], 统一用元素写法 |
实操建议:拿到一批 SMILES 后,先用正则过滤一遍特殊结构,再统一转成标准芳香性表示。这步能提前规避很多问题。
4.6 代码实操:用 RDKit 处理这些特殊结构
直接上代码——用 RDKit 解析和转化上面提到的三种情况:
from rdkit import Chem
smiles_list = [
"c1ccccc1", # 芳香苯
"C1=CC=CC=C1", # Kekulé 式苯
"[Na+].[Cl-]", # 离子对
"[13C](=O)O", # 同位素标记甲酸
"c1cc[nH]c1", # 吡咯
]
for smi in smiles_list:
mol = Chem.MolFromSmiles(smi)
if mol:
# 保持芳香形式
arom_smi = Chem.MolToSmiles(mol)
# Kekulize 后输出
kek_smi = Chem.MolToSmiles(mol, kekuleSmiles=True)
print(f"输入: {smi:20s} -> 芳香: {arom_smi:20s} -> Kekule: {kek_smi}")
else:
print(f"解析失败: {smi}")
输出效果:
输入: c1ccccc1 -> 芳香: c1ccccc1 -> Kekule: C1=CC=CC=C1
输入: C1=CC=CC=C1 -> 芳香: c1ccccc1 -> Kekule: C1=CC=CC=C1
输入: [Na+].[Cl-] -> 芳香: [Na+].[Cl-] -> Kekule: [Na+].[Cl-]
输入: [13C](=O)O -> 芳香: [13C](=O)O -> Kekule: [13C](=O)O
输入: c1cc[nH]c1 -> 芳香: c1cc[nH]c1 -> Kekule: C1=CC[NH]C=C1
注意第三行,离子化合物的表示在两种模式下一模一样——这很合理,因为电荷和同位素信息跟芳香性无关。
核心要点:芳香环、离子电荷、同位素标签可以同时出现在一个 SMILES 里。RDKit 的 MolToSmiles() 会保留所有信息。你只需要保证书写格式正确,解析就不会丢数据。
4.7 本章知识结构 SVG
这张图把三块知识串起来了。芳香环、离子电荷、同位素标签既是独立的表示方法,又会在真实分子中组合出现。