解析文件的最佳方法


9

我正在尝试寻找一种更好的解决方案,以对其中一些著名的文件格式进行解析,例如:EDIFACTTRADACOMS

如果您不熟悉这些标准,请查看Wikipedia的以下示例:

参见以下有关用于回答产品可用性请求的EDIFACT消息的示例:-

UNA:+.? '
UNB+IATB:1+6XPPC+LHPPC+940101:0950+1'
UNH+1+PAORES:93:1:IA'
MSG+1:45'
IFT+3+XYZCOMPANY AVAILABILITY'
ERC+A7V:1:AMD'
IFT+3+NO MORE FLIGHTS'
ODI'
TVL+240493:1000::1220+FRA+JFK+DL+400+C'
PDI++C:3+Y::3+F::1'
APD+714C:0:::6++++++6X'
TVL+240493:1740::2030+JFK+MIA+DL+081+C'
PDI++C:4'
APD+EM2:0:130::6+++++++DA'
UNT+13+1'
UNZ+1+1'

UNA段是可选的。如果存在,它将指定将用于解释消息其余部分的特殊字符。UNA后面有六个字符,顺序如下:

  • 组件数据元素分隔符(在此示例中:)
  • 数据元素分隔符(此示例中的+)
  • 十进制通知(此示例中的。)
  • 释放字符(此示例中的?)
  • 保留,必须为空格
  • 段终止符(此示例中的')

如您所见,这只是一些以特殊方式格式化的数据等待解析(非常类似于XML文件)。

现在我的系统是建立在PHP之上的,并且我能够为每个段使用正则表达式创建解析器,但是问题不是每个人都能完美地实现标准。

一些供应商倾向于完全忽略可选的细分市场和领域。其他人可能选择发送比其他人更多的数据。这就是为什么我不得不为段和字段创建验证器以测试文件是否正确。

您可以想象我现在正在遇到的正则表达式的噩梦。另外,每个供应商都需要对正则表达式进行很多修改,我倾向于为每个供应商构建一个解析器。


问题:

1-这是解析文件(使用正则表达式)的最佳实践吗?

2-是否有更好的解析文件的解决方案(也许那里有现成的解决方案)?它能否显示缺少的段或文件已损坏?

3-如果仍然要构建解析器,应该使用哪种设计模式或方法?

笔记:

我读过有关yacc和ANTLR的文章,但不知道它们是否符合我的需求!


看完EDIFACT语法,解析器和库(Java)之后,我想知道使用词法分析器/解析器是否可以工作。如果是我,我将首先尝试解析器组合器。:)
Guy Coder'5

Answers:


18

您需要的是一个真正的解析器。正则表达式处理词法分析,而不是语法分析。也就是说,它们在您的输入流中标识令牌。解析是令牌的上下文,即IE谁去向何处以及以什么顺序。

经典的解析工具是yacc / bison。经典的词法分析器是lex / flex。由于php允许集成C代码,因此您可以使用flex和bison来构建解析器,让php在输入文件/流上调用它,然后获得结果。

一旦了解了这些工具,它将会快速发展,并且使用起来会容易得多。我建议阅读Lex and Yacc 2nd Ed。来自O'Reilly。例如,我在github上创建了一个makefile 和flex and bison项目。如有必要,它可用于Windows交叉编译。

复杂,但是正如您所发现的,您需要做的事情很复杂。为使解析器正常工作,必须完成大量的“工作”,而flex和bison处理机械位。否则,您会发现自己处在与汇编相同的抽象层上编写代码的位置。


1
+1不错的答案,尤其是考虑到它带有示例解析器。
卡勒布(Caleb)2012年

@caleb谢谢,我经常使用flex / bison,但是很少有像样的(复杂的)示例。这是有史以来最好的解析器,因为注释不多,请随时发送更新。
Spencer Rathbun'5

@SpencerRathbun非常感谢您的详细回答和示例。我不了解您提到的任何术语(yacc / bison,lex / flex等),因为我的经验主要是关于Web开发的。是“Lex和Yacc第二版”足以让我明白了一切,并建立了良好的解析器?还是我应该首先介绍其他主题和材料?
松戈2012年

@songo这本书确实涵盖了所有相关细节,而且篇幅很短,只有300页左右的中型页面。它不涉及使用c或语言设计。幸运的是,有很多c引用可供使用,例如K&R The C Programming Language,而您无需设计一种语言,只需遵循所引用的标准即可。请注意,建议您阅读每本书的封面,因为作者将只提及一次,并假定您需要时可以回头再读一遍。这样您就不会错过任何东西。
Spencer Rathbun 2012年

我认为标准词法分析器无法处理UNA行可能指定的动态分隔符。因此,至少需要5个带有运行时可自定义字符的词法分析器。
凯文(Kevin)

3

ouch ..“ true”解析器?状态机??

抱歉,但是自从我开始工作以来,我已经从大学转变为黑客..所以我会说有更简单的方法..尽管在学术上可能不那么“精炼” :)

我将尝试提供一种可能或可能不同意的替代方法,但在工作环境中可能非常实用。

我会;

loop every line
   X = pop the first 3 letters of line
   Y = rest of line
   case X = 'UNA':
       class init (Y)

从那里我将使用类的数据类型。拆分组件和元素分隔符并遍历返回的数组。

对我来说,这是代码重用,面向对象,低内聚和高度模块化..易于调试和编程。越简单越好。

要解析文件,您不需要状态机或任何完全复杂的..状态机非常适合解析代码,当您在OO上下文中使用上述pseduo代码时,您会感到惊讶。

ps。我之前使用过非常相似的文件:)


更多伪代码发布在这里:

UNA:

init(Y):
 remove ' from end
 components = Y.split(':') 
 for c in components
     .. etc..

 getComponents():
   logic..
   return

 getSomethingElse():
   logic..
   return

class UNZ:
   ...

Parser(lines):

Msg = new obj;

for line in lines
   X = pop the first 3 letters of line
   Y = rest of line
   case X = 'UNA':
      Msg.add(UNA(Y))

msg.isOK = true
return Msg

然后您可以像这样使用它。

msg = Main(File.getLines());
// could put in error checking
// if msg.isOK:
msg.UNA.getSomethingElse();

并说您有多个细分受众群。.使用队列添加它们,并根据需要获取第一,第二等。您实际上只是将味精表示为obj,并提供了对象方法来调用数据。您还可以通过创建自定义方法来利用这一点..进行继承..嗯,这是一个不同的问题,如果您理解它,我认为您可以轻松应用


3
我之前已经做过,发现除了一两个案例的情况下,它是不够的recognize X token and do Y。没有上下文,您不能有多个状态,只有很少的情况会使代码blo肿,错误处理也很困难。我发现我几乎在所有情况下都需要这些功能。随着复杂性的增加,在其中留下了错误。最难的部分是建立骨架,并学习该工具的操作方式。超越它,它就像鞭打东西一样快。
Spencer Rathbun'5

这是一条消息,您需要什么状态?看起来这样的消息是由复合结构和段结构组织的,非常适合这种面向对象的方法。错误处理是按类完成的,并且正确完成了,您可以构造一个非常有效且可扩展的解析器。诸如此类的消息适合于类和函数,尤其是当多个供应商发送相同格式的不同样式时。一个示例是UNA类中的函数,该函数为特定供应商返回了特定值。
罗斯

@Ross所以基本上你将有一个“UNA类”该段“UNA”和里面会有每个厂商解析方法(parseUNAsegemntForVendor1()parseUNAsegemntForVendor2()parseUNAsegemntForVendor3(),...等),对不对?
松戈2012年

2
@Ross消息的各个部分,在解析过程中的不同时间点有效。这些就是我在谈论的状态。OO设计很聪明,我并不是说它不起作用。我之所以选择flex和bison,是因为像函数式编程概念一样,它们比其他工具更适合,但大多数人认为它们过于复杂,以至于不便于学习。
Spencer Rathbun'5

@Songo ..不,您将独立于供应商进行解析(除非您是新用户)。解析将在该类的INIT中进行。您可以根据用于构造消息的相同规则将消息转换为数据对象。但是,如果您需要从消息中获取某些信息,并且在各个供应商中它的表示方式有所不同,那么您将拥有不同的功能。但是为什么这样呢?使用基类并为每个供应商提供一个单独的类,仅在必要时才进行覆盖,这非常容易。利用继承。
罗斯

1

您是否尝试过搜索“ PHP EDIFACT”?这是弹出的第一批结果之一:http : //code.google.com/p/edieasy/

虽然这可能不足以满足您的用例,但您可能可以从中获得一些想法。我不喜欢其中有许多嵌套的for循环和条件的代码,但这可能是一个开始。


1
我在那里检查了许多项目,但是问题主要出在使用该标准的供应商的不同实现上。我可能会强迫一个供应商向我发送特定细分受众群,但我可能认为它对于另一供应商而言是可选的。这就是为什么我可能仍然需要构建自己的自定义解析器的原因。
松戈2012年

1

既然自从提到Yacc / Bison + Flex / Lex之后,我不妨抛出其他主要替代方法之一:解析器组合器。它们在像Ha​​skell这样的函数式编程中很流行,但是如果您可以连接C代码,则可以使用它们,而且您知道,有人也为PHP编写了一个。 (我没有使用该特定实现的经验,但是如果它像大多数实现一样工作,那应该很好。)

一般的概念是从一组小型的,易于定义的解析器开始,通常是分词器。就像您对提到的6个数据元素中的每一个都有一个解析器功能。然后,您可以使用组合器(组合函数的函数)来制作可捕获较大元素的较大解析器。类似于可选段的是optional在段解析器上运行的组合器。

不确定它在PHP中的运行情况如何,但这是编写解析器的一种有趣方式,我非常喜欢以其他语言使用它们。


0

而不是摆弄正则表达式创建自己的状态机

在非平凡的情况下,这将更具可读性(并能够提供更好的注释),并且更容易调试正则表达式的黑匣子


5
快速说明一下,这就是flex和bison的内幕。只有他们做对了
Spencer Rathbun'5

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.