【问题标题】:Merge two large strings in python efficiently有效地合并python中的两个大字符串
【发布时间】:2019-09-16 15:16:00
【问题描述】:

我已经为此工作了几天,似乎没有我需要的答案。

由于担心这被标记为重复,我将解释为什么其他问题对我不起作用。

  • DIFFLIB for Python 的任何答案都无法满足我的需求。 (我在下面描述更多)。它太慢了——除非有人对我有很好的优化技巧(unified_diff 模块),否则我将无法使用它。

  • 我尝试研究如何将大字符串发送到需要文件的命令,但没有一个选项适合我。如果我可以让它工作,我不介意使用这个选项(也在下面有更多描述)。

  • 我不介意被标记为重复,只要它是一个真正解决我的问题的问题 - 我已经抓取了一些网站,但还没有找到适合我的解决方案。

我想在 Python 中合并两个大字符串。每个字符串大约 1.5KB。假设有两个字符串,str1 和 str2,我只想返回 merged 字符串,它只是 str1 与 str2 的添加信息。我不想删除任何东西。

在大多数情况下,这些字符串相对相同。大多数时候,90% 是一样的。不同的是,第二个字符串中可能添加了新信息,我想将该信息捕获到原始字符串中。

所以。

str1 = "This is a very
        Long string and
        This is how it looks."

str2 = "This is a very
        This is my Example
        This is how it looks."

result = "This is a very
          Long string and
          This is my Example 
          This is how it looks." #Third line was added to str1

我解决这个问题的第一个方法是使用 git diff。我在 Windows 上,我要做的是使用我将字符串输出到的临时文件执行 git diff cmd,然后立即删除这些文件。我制作的 cmd 函数会将输出(统一差异)作为字符串返回。然后我会在字符串上发布处理以删除差异总是添加的标题。通过将输出指示符更改为空格,我能够删除每行上的“+”和“-”(为简单起见,我从代码中使用了所有选项。

#The f1and f2 text files are created here
#cmd is a function created by me, and it uses the os module to execute the command

output = cmd("git diff -U999999 -b --no-index f1.txt f2.txt")

#f1 and f2 text files are deleted here

我尝试过 DiffLib,但速度太慢了。完成一个差异文件输出大约需要 8-10 分钟。我使用了unified_diff 模块,并将参数作为字符串和列表传递。我什至试图操纵源代码,但我的更改并没有让它变得更快。

我也尝试将字符串直接传递给 git diff 或只是 diff。但是,会出现错误,抱怨 “Argument List too Long”。我什至尝试将字符串发送到标准输出并将其用作文件参数,但这也没有太大作用。

如果可以调整以实现我的目标,我不介意使用这些选项中的任何一个。显然,我目前的解决方案(上面的代码块)效率很低,如果可以避免的话,我不想继续创建和删除文本文件。

【问题讨论】:

  • 如果strA 是“A \n B \n C \n D` 并且strB 是“A \n E \n F \f D”,那么结果应该是什么? A B C E F DA B E C F D?
  • @alex 我应该在上面提供更多详细信息。这两个字符串几乎相同。但是,第二个字符串会定期添加信息。我想捕获新添加的信息并将其添加到原始字符串中。
  • 你试过github.com/google/diff-match-patch吗?我可以想象你的问题规模太大了。
  • 您是否始终知道哪个字符串是“原始”字符串以及哪个字符串有“额外”添加?
  • @JohnGordon 是的,我永远知道两者之间的区别

标签: python python-3.x string git python-2.7


【解决方案1】:

如果您想推出自己的解决方案 - 您可以将每一行添加到某个列表中,一次一个,在第一个字符串和第二个字符串之间交替:

list_1 = "A\nB\nC\nD".split()
list_2 = "A\nE\nF\nD".split()
output = []

for i in range(len(list_1)):
    output.append(list_1[i])
    output.append(list_2[i])

for o in output:
    print(o)

>> A
>> A
>> B
>> E
>> C
>> F
>> D
>> D

然后您需要从输出列表中删除重复项(不使用集合,因为集合会打乱顺序)。

from collections import OrderedDict

output = list(dict.fromkeys(output))

for o in output:
    print(o)

>> A
>> B
>> E
>> C
>> F
>> D

我能想到的一些注意事项:

  1. 如果是len(list_1) != len(list_2),您需要考虑这一点。

  2. 我不清楚在这种情况下“合并”是什么意思。例如,如果:

    list_1 == ["A", "B", "A", "D", "A", "C", "A", "D", "A", "B", "B", "B"]
    list_2 == ["B", "A", "C", "C", "A", "A", "D", "D", "A", "B", "C", "A", "D"]
    

我不清楚生成的合并应该是什么样子。

【讨论】:

  • 合并将是相对相同的。为了澄清起见,这些字符串来自我不想编辑的文件,只是从中读取。说-file1 和file2。每个文件都包含信息块。大多数情况下,这些文件是相同的。但是,有时会增加一两个信息块。因此,在您的示例中,列表永远不会那么不同。它们只会在某处之间包含额外的线条。是的,它们的长度可能不同。我对字符串进行了预处理,阻止我只使用带有差异的直接文件。
【解决方案2】:

在@schwobaseggl 的建议的帮助下,我找到了解决方案。

Diff_patch_match 比 difflib 快 光年

对于可能遇到类似问题的任何人,以下是我遇到的一些障碍以及我如何解决它们:

  • 创建 diff_patch_match 对象时,请务必将字符串作为参数传递。
  • 如果您的输入只是一行,那么您不必担心我的下一点。但是,如果您的字符串是多行(由 '\n' 分隔),那么您必须遵循不同的协议。该网站链接here。但是,如果网站出现故障,我会将其放在下面。链接的网站使用的是 Java,但我在这里将其更改为 Python。
def diff_lineMode(text1, text2):
  dmp = new diff_match_patch()
  a = dmp.diff_linesToChars_(text1, text2)
  lineText1 = a[0]
  lineText2 = a[1]
  lineArray = a[2]
  diffs = dmp.diff_main(lineText1, lineText2, false)
  dmp.diff_charsToLines_(diffs, lineArray)
  return diffs

感谢大家的cmets和回答!

【讨论】:

    猜你喜欢
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-05
    • 2015-09-01
    • 2019-06-29
    • 1970-01-01
    • 2010-11-03
    相关资源
    最近更新 更多