【问题标题】:Highlight or bold strings in a text file using python-docx?使用 python-docx 在文本文件中突出显示或加粗字符串?
【发布时间】:2021-10-18 16:01:26
【问题描述】:

我有一个“短字符串”列表,例如:

['MKWVTFISLLLLFSSAYSRGV', 'SSAYSRGVFRRDTHKSEIAH', 'KPKATEEQLKTVMENFVAFVDKCCA']

我需要匹配一个包含在 word 文件 (BSA.docx) 或 .txt 文件(无关紧要)中的“长字符串”,例如:

sp|P02769|ALBU_BOVIN 白蛋白 OS=Bos taurus OX=9913 GN=ALB PE=1 SV=4 MKWVTFISLLLLFSSAYSRGVFRRDTHKSEIAHRFKDLGEEHFKGLVLIAFSQYLQQCPFDEHVKLVNELTEFAKTCVADESHAGCEKSLHTLFGDELCKVASLRETYGDMADCCEKQEPERNECFLSHKDDSPDLPKLKPDPNTLCDEFKADEKKFWGKYLYEIARRHPYFYAPELLYYANKYNGVFQECCQAEDKGACLLPKIETMREKVLASSARQRLRCASIQKFGERALKAWSVARLSQKFPKAEFVEVTKLVTDLTKVHKECCHGDLLECADDRADLAKYICDNQDTISSKLKECCDKPLLEKSHCIAEVEKDAIPENLPPLTADFAEDKDVCKNYQEAKDAFLGSFLYEYSRRHPEYAVSVLLRLAKEYEATLEECCAKDDPHACYSTVFDKLKHLVDEPQNLIKQNCDQFEKLGEYGFQNALIVRYTRKVPQVSTPTLVEVSRSLGKVGTRCCTKPESERMPCTEDYLSLILNRLCVLHEKTPVSEKVTKCCTESLVNRRPCFSALTPDETYVPKAFDEKLFTFHADICTLPDTEKQIKKQTALVELLKHKPKATEEQLKTVMENFVAFVDKCCAADDKEACFAVEGPKLVVSTQTALA

我想使用python(在终端或jupyter笔记本中)获得以下内容:

  1. 在长字符串中突出显示较短的字符串匹配项。高亮样式并不重要,它可以用黄色标记或粗体或下划线突出显示,任何跳到眼睛看是否匹配的东西。

  2. 求长字符串的覆盖率为((突出显示的字符数)/(长字符串的总长度))*100。注意长字符串的“>>”开头的第一行只是一个标识符,需要忽略。

这是我当前第一个任务的代码:

from docx import Document

doc = Document('BSA.docx')

peptide_list = ['MKWVTFISLLLLFSSAYSRGV', 'SSAYSRGVFRRDTHKSEIAH', 'KPKATEEQLKTVMENFVAFVDKCCA']

def highlight_peptides(text, keywords):
    text = text.paragraphs[1].text
    replacement = "\033[91m" + "\\1" + "\033[39m"
    enter code here`text = re.sub("(" + "|".join(map(re.escape, keywords)) + ")", replacement, text, flags=re.I)
    

highlight_peptides(doc, peptide_list)

问题在于列表中的前两个短字符串重叠,结果中只有第一个在序列中以红色突出显示。

请参阅下面的第一个链接,其中包含我正在获得的输出结果。

current result

查看第二个链接以可视化我的“理想”结果。

ideal result

在理想情况下,我还包括了寻找序列覆盖率的第二个任务。我不知道如何计算彩色或突出显示的字符。

【问题讨论】:

  • “突出显示”是什么意思?您的输出将如何呈现?您是否将其用作终端中的脚本? Jupyter笔记本?网络应用?你试过什么了?判断一个字符串是否是另一个字符串的子字符串并不难。
  • 您好 ddejohn,感谢您的回复。我包括了额外的解释和我写的当前代码。

标签: python text highlight


【解决方案1】:

您可以使用third-party regex module 进行重叠关键字搜索。然后,最简单的方法可能是通过 2 次遍历来完成匹配:(1) 存储每个突出显示的片段的开始和结束位置,并组合任何重叠的部分:

import regex as re # important - not using the usual re module

def find_keywords(keywords, text):
    """ Return a list of positions where keywords start or end within the text. 
    Where keywords overlap, combine them. """
    pattern = "(" + "|".join(re.escape(word) for word in keywords) + ")"
    r = []
    for match in re.finditer(pattern, text, flags=re.I, overlapped=True):
        start, end = match.span()
        if not r or start > r[-1]: 
            r += [start, end]  # add new segment
        elif end > r[-1]:
            r[-1] = end        # combine with previous segment
    return r

positions = find_keywords(keywords, text)

您的“关键字覆盖率”(突出显示的百分比)可以计算为:

coverage = sum(positions[1::2]) - sum(positions[::2]) # sum of end positions - sum of start positions
percent_coverage = coverage * 100 / len(text)

然后 (2) 将格式添加到文本中,使用 run properties in docx:

import docx

def highlight_sections_docx(positions, text):
    """ Add characters to a text to highlight the segments indicated by
     a list of alternating start and end positions """
    document = docx.Document()
    p = document.add_paragraph()
    for i, (start, end) in enumerate(zip([None] + positions, positions + [None])):
        run = p.add_run(text[start:end])
        if i % 2:  # odd segments are highlighted
            run.bold = True   # or add other formatting - see https://python-docx.readthedocs.io/en/latest/api/text.html#run-objects
    return document

doc = highlight_sections_docx(positions, text)
doc.save("my_word_doc.docx")

或者,您可以突出显示 html 中的文本,然后使用 htmldocx 包将其保存到 Word 文档中:

def highlight_sections(positions, text, start_highlight="<mark>", end_highlight="</mark>"):
    """ Add characters to a text to highlight the segments indicated by
     a list of alternating start and end positions """
    r = ""
    for i, (start, end) in enumerate(zip([None] + positions, positions + [None])):
        if i % 2:  # odd segments are highlighted
            r += start_highlight + text[start:end] + end_highlight
        else:      # even segments are not
            r += text[start:end]
    return r

from htmldocx import HtmlToDocx
s = highlight_sections(positions, text, start_highlight="<strong>", end_highlight="</strong>")
html = f"""<html><head></head><body><span style="width:100%; word-wrap:break-word; display:inline-block;">{s}</span></body></html>"""
HtmlToDocx().parse_html_string(html).save("my_word_doc.docx")

&lt;mark&gt; 将是比 &lt;strong&gt; 更合适的 html 标记,但不幸的是 HtmlToDocx 不保留 &lt;mark&gt; 的任何格式,并忽略 CSS 样式)。

highlight_sections也可以用来输出到控制台:

print(highlight_sections(positions, text, start_highlight="\033[91m", end_highlight="\033[39m"))

... 或 Jupyter / IPython 笔记本:

from IPython.core.display import HTML
s = highlight_sections(positions, text)
display(HTML(f"""<span style="width:100%; word-wrap:break-word; display:inline-block;">{s}</span>""")

【讨论】:

  • 感谢您的解决方案。如果我在终端中打印,第一部分的工作原理与我应该做的完全一样。虽然关键字覆盖率有点偏,但我相信,因为虽然如果我计算字符时可以在笔记本中显示“红色”格式,但它会解释为字符串,例如包含这些格式字符 x1b[39m\x1b[39m。此外,如果我尝试将其保存在文本文件中,它也会按字面意思解释这些字符。
  • 关键字覆盖率计算基于添加任何格式之前的位置,但可能会受到文档中已有格式的影响。
  • 你是对的,它实际上工作得很好。谢谢。
  • 有没有办法将格式保存到 txt 文件或 docx 中?一旦我尝试,它将 ASCII 代码解释为字符串。我发现的一种解决方法是修改“highlight_sections”函数,以便以小写形式返回“奇数段”而不是突出显示。 if i % 2: # 奇数段高亮 r += text[start:end].lower().
  • 格式字符串可以保存在文本文件中,以后可以从中检索,但大多数文本编辑器不会应用格式,而是将\033 更改为特殊字符。你最喜欢的输出到底是什么?如果您不确定或需要多个输出,我建议使用 HTML,因为这可以轻松输出到 Jupyter 笔记本或转换为 Word 文档,或按原样保存。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-12
  • 2015-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-13
相关资源
最近更新 更多