Python:仅替换字符串中的一个实例

1 投票
2 回答
563 浏览
提问于 2025-04-17 16:45

我有一些样本数据,长得像这样:

ATOM    973  CG  ARG A  61     -21.593   8.884  69.770  1.00 25.13           C
ATOM    974  CD  ARG A  61     -21.610   7.433  69.314  1.00 23.44           C
ATOM    975  NE  ARG A  61     -21.047   7.452  67.937  1.00 12.13           N

我想要替换第六列,只有第六列,替换的方式是把一个偏移值加上去,在这个例子中,偏移值是308。

所以61加上308等于369,因此第六列中的61应该被替换成369。

我不能用 str.split() 来分割这一行,因为行之间的空格非常重要。

我试过用 str.replace(),但是第二列的值可能会和第六列的值重叠。

我也尝试过把行反转,然后用 str.replace(),但第七、八、九、十和十一列的值也可能和要替换的字符串重叠。

到目前为止,我写的代码很糟糕(除了在第七、八、九、十和/或十一列的值重叠的情况下,部分有效):

with open('2kqx.pdb', 'r') as inf, open('2kqx_renumbered.pdb', 'w') as outf:
    for line in inf:
        if line.startswith('ATOM'):
            segs = line.split()
            if segs[4] == 'A':
                offset = 308
                number = segs[5][::-1]
                replacement = str((int(segs[5])+offset))[::-1]
                print number[::-1],replacement
                line_rev = line[::-1]
                replaced_line = line_rev.replace(number,replacement,1)
                print line
                print replaced_line[::-1]
                outf.write(replaced_line[::-1])

上面的代码产生了下面的输出。你可以看到在第二行,第六列没有被改变,但在第七列却被改变了。我原本以为通过反转字符串可以避免与第二列的重叠,但我忘记了其他列的情况,我真的不知道该怎么解决这个问题。

ATOM    973  CG  ARG A  369     -21.593   8.884  69.770  1.00 25.13           C
ATOM    974  CD  ARG A  61     -21.3690   7.433  69.314  1.00 23.44           C
ATOM    975  NE  ARG A  369     -21.047   7.452  67.937  1.00 12.13           N

2 个回答

1

你最好不要自己尝试解析 PDB 文件。

使用一个 PDB 解析器。市面上有很多免费的解析器,通常包含在生物或计算化学的软件包中,比如说

biopython

如果你的输入文件是 raw.pdb,那么用 biopython 来处理的方法如下:

from Bio.PDB import PDBParser, PDBIO
parser=PDBParser()
structure = parser.get_structure('some_id', 'raw.pdb')
for r in structure.get_residues():
    r.id = (r.id[0], r.id[1] + 308, r.id[2])
io = PDBIO()
io.set_structure(structure)
io.save('shifted.pdb')

我查了一下,发现了一个快速解决你特定问题的方法(不需要第三方依赖):

http://code.google.com/p/pdb-tools/

在这里有很多其他有用的 pdb-python 脚本工具,其中有一个脚本 pdb_offset.py

这个脚本是独立的,我只是复制了它的 pdb_offset 方法来演示它的工作原理,你的三行示例代码在 raw.pdb 中:

def pdbOffset(pdb_file, offset):
    """
    Adds an offset to the residue column of a pdb file without touching anything
    else.
    """

    # Read in the pdb file
    f = open(pdb_file,'r')
    pdb = f.readlines()
    f.close()

    out = []
    for line in pdb:
        # For and ATOM record, update residue number
        if line[0:6] == "ATOM  " or line[0:6] == "TER   ":
            num = offset + int(line[22:26])
            out.append("%s%4i%s" % (line[0:22],num,line[26:]))
        else:
            out.append(line) 

    return "".join(out)


print pdbOffset('raw.pdb', 308)

它会输出

ATOM    973  CG  ARG A 369     -21.593   8.884  69.770  1.00 25.13           C
ATOM    974  CD  ARG A 369     -21.610   7.433  69.314  1.00 23.44           C
ATOM    975  NE  ARG A 369     -21.047   7.452  67.937  1.00 12.13           N
2

data = """\
ATOM    973  CG  ARG A  61     -21.593   8.884  69.770  1.00 25.13           C
ATOM    974  CD  ARG A  61     -21.610   7.433  69.314  1.00 23.44           C
ATOM    975  NE  ARG A  61     -21.047   7.452  67.937  1.00 12.13           N"""

offset = 308
for line in data.split('\n'):
    line = line[:22] + "  {:<5d}  ".format(int(line[22:31]) + offset) + line[31:]
    print line

我没有精确计算空格的数量,这只是一个大概的估计。如果你想要比代码中随意出现的数字22和31更灵活的方式,你需要找到一个方法来确定你的开始和结束位置(不过这和我假设数据是固定列格式的想法是相反的)。

撰写回答