我在Flex / AS3上工作(为简单起见)是一个XML编辑器。我需要提供撤消/重做功能。
当然,一种解决方案是在每次编辑时存储整个源文本。但是,为了节省内存,我想改为存储差异(这些差异也将用于将更新传输到服务器以进行自动保存)。
我的问题是 - 我可以使用明文差异算法来跟踪这些XML更改吗?
我在互联网上的研究表明我不能这样做。但是,我显然错过了一些东西。明文差异提供了据称的功能:
diff(text, text') -> diffs
patch(text, diffs) -> text'
XML只是文本,为什么我不能只使用diff()和patch()来可靠地转换文本?
例如:让我们说我是一位诗人。当我写诗时,我使用了许多时髦的标点符号...你知道,比如<,/和>。 (你可能会看到我要去的地方...)如果我在一个使用差异提供撤消/重做功能的应用程序中编写我的诗歌,当我撤消/重做我的编辑时,我的诗歌会变得乱码吗?这只是文字!为什么它会对算法产生影响?
我显然在这里得不到什么......谢谢你的解释! :)
更新
我遇到过一些关于使用明文算法区分XML的讨论:
另外,我知道Command模式可能是实现Undo / Redo的更好方法。为简单起见,我简化了我的用例,我仍然认为XML差异是最好的方法。
答案 0 :(得分:15)
我是Google的纯文本差异/匹配/补丁库的作者。
关键问题是你的补丁是否准确。在理想的世界中:
diff(old_text, new_text) -> edits
patch(edits, old_text) -> new_text
请注意,两个操作中的基本文本(old_text)是相同的。在这种理想情况下,无论内容的类型如何,简单的纯文本差异和补丁都将完美地工作。如果这种情况适用于你,那么你已经完成了。
问题在于模糊修补。这是相应的例子:
diff(old_text, new_text) -> edits
patch(edits, old_forked_text) -> new_forked_text
请注意,两个操作中的基本文本不同。它们应该是相似的,但补丁操作现在必须使用“判断”它应该做什么。一些补丁可能完全符合编辑中指定的要求,其他补丁可能需要针对位置进行调整,其他补丁可能需要针对更改的上下文进行调整,其他补丁可能根本不适合应该被删除。如果您的修补算法在做出决策时不了解XML的结构,那么最终可能会遇到错误的XML。这是一个示例:
old_text = Jabberwock<SPAN>Hello<SPAN>World</SPAN></SPAN>
new_text = Jabberwock<DIV>Hello<SPAN>World</SPAN></DIV>
diff(old_text, new_text) -> edits
edits = ["SPAN" -> "DIV" @ character 11,
"SPAN" -> "DIV" @ character 41]
old_forked_text = <SPAN>Hello<SPAN>World</SPAN></SPAN>
patch(edits, old_forked_text) -> new_forked_text
new_forked_text = <SPAN>Hello<DIV>World</SPAN></DIV>
让我们仔细看看这个。原始差异返回两个编辑,将最外面的SPAN更改为DIV。简单的改变。不幸的是,正在应用此编辑的文本已从原始文本更改。 “Jabberwock”一词已被删除。现在,第一个SPAN-&gt; DIV更改与第二个SPAN标记匹配,而不是第一个。由于补丁算法不了解XML规则,因此会导致非法嵌套标记。
有一些hacks允许你在使用纯文本补丁时保证有效的XML,但是它们会导致一些灵活性的损失(原始问题已经链接到我写的关于此的wiki页面)。修补XML的最终解决方案当然是使用XML感知的差异和补丁算法。这些更加复杂和昂贵,但它们存在。 Google的名字是Tancred Lindholm和SebastianRönnau,他们在XML领域所做的出色工作(特别是关于DocEng)。
请告诉我是否还有其他任何内容可以添加。
- 尼尔弗雷泽
答案 1 :(得分:1)
我一直使用Beyond Compare来比较XML文档。它在一定程度上理解XML。
您可能需要对这两个文档进行预处理,以便进行文本比较,以尽可能做到最好。例如,在某些XML文档中,某些元素的顺序可能无关紧要。你的差异工具肯定很重要!在比较两个已排序的文件之前,您可能需要使用XML转换器对XML进行预处理,XML转换器将这些元素排序为两个文件中的通用顺序。
您还希望对两个文档使用相同的缩进。我发现在新行上启动每个元素很有用,并且对每个级别使用相同数量的缩进(带空格)。如果您的文档非常深,您可能希望每个级别只使用一个或两个空格,以便比较适合屏幕。您甚至可能希望每行使用一个属性(并将属性排序为常用顺序)。
答案 2 :(得分:1)
如果您是撤消/重做点之间数据的唯一“拥有者”,那么您当然可以使用明文差异。正如你所指出的,它相当于一组转换。
然而,根据您提供的操作,明文差异可能不是远程接近录制撤消/重做的最佳选择,您可能需要专门化某些情况。想象一下,只需记录一个ReplaceAll命令,它可能只有几个字节的开销加上搜索和替换字符串。这可能会产生大量的明文差异。
在更广泛的上下文中,如果您允许外部编辑这些文档,并且您正在考虑如何在服务器上存储增量,那么您将模仿git或其他版本控制系统。你必须使用某种diff算法,因为记录你的命令显然不是转换的唯一来源。此时,您开始将撤消/重做与版本控制混合使用,您可能想要为用户混淆这些概念。
我会在编辑会话中保持撤消/重做,并在文件打开时禁止外部编辑。如上所述,这使您可以针对广泛的案例优化命令记录。
除此之外,要么使用传统的版本控制(考虑包装git),要么实现自己的方式来处理编辑器外部更改的文件。
答案 3 :(得分:-1)
我认为你可以将文本差异用于xml,尤其是在人类将逐行编写xml的情况下。我不知道你说的信息是什么,你不能这样做但我想这个陈述是基于这样一个事实,即空格字符(空格,制表符,换行符......)与纯文本文件有些不同,从XML的角度来看,可能会导致两个不同的文本文件相同。但同样,对于一个针对人类的编辑,我不明白为什么你不能。