根据某些特定规则对文本进行标记。 C ++中的算法

时间:2009-05-24 05:13:06

标签: c++ algorithm tokenize

我正在编写一个程序,它会根据一些特定的规则对输入文本进行标记。我正在使用C ++。

规则

Letter 'a' should be converted to token 'V-A'
Letter 'p' should be converted to token 'C-PA'
Letter 'pp' should be converted to token 'C-PPA'
Letter 'u' should be converted to token 'V-U'

这只是一个示例,实时我有大约500多个这样的规则。如果我提供的输入为“ appu ”,则应将其标记为“ V-A + C-PPA + V-U ”。我已经实现了这样做的算法,并希望确保我做的正确。

算法

所有规则都将保存在XML文件中,并带有与令牌相对应的映射。像

这样的东西
<rules>
  <rule pattern="a" token="V-A" />
  <rule pattern="p" token="C-PA" />
  <rule pattern="pp" token="C-PPA" />
  <rule pattern="u" token="V-U" />
</rules>

1 - 当应用程序启动时,请读取此xml文件并将值保存在“ std :: map ”中。这将在应用程序结束之前可用(单例模式实现)。

2 - 迭代输入文本字符。对于每个角色,寻找匹配。如果找到,变得更贪婪并通过从输入文本中获取下一个字符来寻找更多匹配。这样做直到我们得到一个不匹配。因此,对于输入文本“ appu ”,首先要查找“ a ”的匹配项。如果找到,请尝试通过从输入文本中获取下一个字符来获得更多匹配。所以它会尝试匹配' ap '并找不到匹配项。所以它才会回归。

3 - 当我们得到一个令牌时,用输入文字替换字母'a'。

4 - 使用输入文本中的其余字符重复步骤2和3。

以下是对步骤

的更简单说明
input-text = 'appu'
tokens-generated=''

// First iteration
character-to-match = 'a'
pattern-found = true

// since pattern found, going recursive and check for more matches
character-to-match = 'ap'
pattern-found = false

tokens-generated = 'V-A'

// since no match found for 'ap', taking the first success and replacing it from input text
input-text = 'ppu'

// second iteration
character-to-match = 'p'
pattern-found = true

// since pattern found, going recursive and check for more matches
character-to-match = 'pp'
pattern-found = true

// since pattern found, going recursive and check for more matches
character-to-match = 'ppu'
pattern-found = false

tokens-generated = 'V-A + C-PPA'

// since no match found for 'ppu', taking the first success and replacing it from input text
input-text = 'u'

// third iteration
character-to-match = 'u'
pattern-found = true

tokens-generated = 'V-A + C-PPA + V-U'  // we'r done!

问题

1 - 此算法是否适用于此问题,还是有更好的方法来解决此问题?

2 - 如果这是正确的方法,std :: map在这里是个不错的选择?或者我是否需要创建自己的键/值容器?

3 - 是否有一个可以像上面那样标记字符串的库?

任何帮助将不胜感激

:)

5 个答案:

答案 0 :(得分:3)

因此,您要查看地图中的所有代币以查找匹配项?你也可以使用列表或数组;无论如何,它都将是一种效率低下的搜索。

找到适合开始或继续比赛的令牌的更有效方法是将它们存储为trie。在那里查找一个字母会给你一个子编辑,其中只包含以该字母作为第一个字母的标记,然后你就可以继续向下搜索。


编辑:让我再解释一下。

首先,我应该解释一下,我不熟悉这些超出名称的C ++ std::map,这使得这是一个完美的例子,说明为什么一个人学习这个东西的理论以及特定的细节。库特别是编程语言:除非该库严重滥用名称“map”(这是不太可能),名称本身告诉我很多关于数据结构的特征。我知道,例如,有一个函数,给定一个键和地图,将非常有效地搜索并返回与该键相关联的值,并且还有一个函数可以为您提供一个列表/ array /所有键中的任何一个,您可以使用自己的代码搜索自己。

我对您的数据结构的解释是,您有一个映射,其中键是您称为模式的键,它们是字符的列表(或数组或某种性质),值是标记。因此,您可以在给定完整模式的情况下快速找到与其关联的令牌。

不幸的是,虽然这样的地图非常适合将XML输入格式转换为内部数据结构,但它并不适合您需要进行的搜索。请注意,您不是查找整个模式,而是查找模式的第一个字符,生成一组可能的标记,然后从第一个模式中生成的模式集中查找模式的第二个字符查找,等等。

所以你真正需要的不是单个地图,而是地图地图的地图,每个地图都用一个字符键入。在顶层查找“p”应该会为您提供一个新地图,其中包含两个键:p,生成C-PPA令牌和“其他任何内容”,生成C-PA令牌。这实际上是一种特里数据结构。

这有意义吗?

如果你首先通过编写解析代码开始,这可能有所帮助:想象别人会编写函数来执行你需要的查找,而且他是一个非常优秀的程序员并且可以做任何与你有关的魔法想。编写解析代码,集中精力使其尽可能简单和干净,使用您需要的任意函数创建任何接口(虽然不会变得微不足道并用一个函数替换整个事物!)。现在,您可以查看最终的查找函数,并告诉您如何访问数据结构,这将引导您获得所需的数据结构类型。一旦你弄明白了,你就可以弄清楚如何加载它。

答案 1 :(得分:1)

  1. 此方法可行 - 我不确定它是否有效,但它应该有效。

  2. 我会使用标准的std :: map而不是你自己的系统。

  3. lex(或flex)等工具可用于此目的。问题在于您是否可以重新生成XML规范更改时将构造的词法分析器。如果XML规范不经常更改,您可以使用lex等工具更轻松地进行扫描和映射。如果XML规范可以随意使用该程序而改变,那么lex可能不太合适。

  4. 有一些警告 - 特别是lexflex生成C代码而不是C ++。

    我还会考虑模式匹配技术 - egrep特别使用的那种东西。这样做的优点是可以在运行时处理(因为egrep始终执行此操作)。或者你可以使用脚本语言 - Perl,Python,...或者你可以考虑类似PCRE(Perl兼容的正则表达式)库。

答案 2 :(得分:1)

你可以使用正则表达式(也许是boost :: regex库)。如果所有模式都只是字母串,那么像“(a | p | pp | u)”这样的正则表达式会找到一个贪婪的匹配。所以:

  1. 使用上述模式运行regex_search以找到下一个匹配项
  2. 将匹配文本插入std :: map以获取替换文本。
  3. 将不匹配的消耗输入和替换文本打印到输出,然后在剩余输入上重复1。
  4. 完成了。

答案 3 :(得分:1)

更好的是,如果您打算使用升级库,那么总会有Boost tokenizer库 - &gt; http://www.boost.org/doc/libs/1_39_0/libs/tokenizer/index.html

答案 4 :(得分:1)

看起来有点复杂,但最有效的方法是使用图表来表示状态图。起初,我认为boost.statechart会有所帮助,但我认为这不合适。使用简单的std :: map IF这个方法可以更有效,因为有很多规则,可能的字符数量有限,而且要读取的文本的长度非常高。

所以无论如何,使用一个简单的图表:

0)用“start”顶点

创建图形

1)读取xml配置文件并在需要时创建顶点(从一个“字符集”(例如“pp”)转换到另一个(例如“ppa”))。在每个顶点内,将转换表存储到下一个顶点。如果“关键文本”已完成,请将顶点标记为final并存储生成的文本

2)现在阅读文本并使用图表进行解释。从“开始”顶点开始。 (*)使用表来解释一个字符并跳转到新的顶点。如果未选择新顶点,则可以发出错误。否则,如果新顶点是最终的,则打印生成的文本并跳回到开始顶点。返回(*),直到没有更多文本要解释。

您可以使用boost.graph来表示图表,但我认为它对您所需要的内容过于复杂。制作自己的自定义代表。