Лучший способ разобрать файл

9

Я пытаюсь найти лучшее решение для создания парсера для некоторых известных форматов файлов, таких как: EDIFACT и TRADACOMS .

Если вы не знакомы с этими стандартами, посмотрите этот пример из Википедии:

Ниже приведен пример сообщения 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, но я не знаю, соответствуют ли они моим потребностям или нет!

Songo
источник
После просмотра этой грамматики EDIFACT, парсеров и библиотек (Java) мне стало интересно, сработает ли использование лексера / парсера. Если бы это был я, я бы сначала попробовал комбинатор парсера. :)
Guy Coder

Ответы:

18

Что вам нужно, так это настоящий парсер. Регулярные выражения обрабатывают лексизм, а не синтаксический анализ. То есть они идентифицируют токены в вашем входном потоке. Парсинг - это контекст токенов, то есть, кто идет куда и в каком порядке.

Классический инструмент синтаксического анализа - yacc / bison . Классический лексер - это lex / flex . Поскольку php позволяет интегрировать код на C , вы можете использовать flex и bison для создания вашего парсера, сделать так, чтобы php вызвал его во входном файле / потоке, а затем получил ваши результаты.

Это будет невероятно быстро , и с ним будет намного легче работать, как только вы поймете инструменты . Я предлагаю прочитать Лекса и Яка 2-е изд. от О'Рейли. Например, я создал проект flex и bison на github с make-файлом. Это кросс-компилируется для окон, если это необходимо.

Это является сложным, но , как вы узнали, что вам нужно сделать , это комплекс. Для правильной работы синтаксического анализатора необходимо выполнить множество «мелочей», а с механическими битами разбираются гибкость и бизон. В противном случае вы окажетесь в незавидном положении написания кода на том же уровне абстракции, что и сборка.

Спенсер Рэтбун
источник
1
+1 Отличный ответ, особенно если учесть, что он поставляется с образцом парсера.
Калеб
@caleb спасибо, я много работаю с flex / bison, но приличных (читай: сложных) примеров очень мало. Это не лучший анализатор, так как комментариев не так много, поэтому не стесняйтесь присылать обновления.
Спенсер Рэтбун,
@SpencerRathbun большое спасибо за ваш подробный ответ и пример. Я ничего не знаю о терминологии, которую вы упомянули (yacc / bison, lex / flex, ... и т. Д.), Поскольку мой опыт в основном связан с веб-разработкой. Является ли «Lex и Yacc второй Ed» достаточно для меня , чтобы понять все и построить хороший анализатор? или есть другие темы и материалы, которые я должен осветить в первую очередь?
Сонго
@songo Книга охватывает все важные детали и довольно коротка, ее размер составляет около 300 средних страниц. Это не распространяется на использование c или языка дизайна . К счастью, доступно множество ссылок на c, таких как K & R The Programming Language, и вам не нужно разрабатывать язык, просто следуйте стандартам, на которые вы ссылались. Обратите внимание, что чтение от обложки к обложке рекомендуется, так как авторы упомянут что-то один раз, и предположим, что если вам это нужно, вы вернетесь и перечитаете. Таким образом, вы ничего не пропустите.
Спенсер Рэтбун,
Я не думаю, что стандартный лексер может обрабатывать динамические разделители, которые может указывать строка UNA. Так что по крайней мере вам понадобится лексер с настраиваемыми во время выполнения символами для 5 разделителей.
Кевин
3

ой .. "настоящий" парсер? конечные автоматы ??

извините, но с тех пор, как я начал работать, меня превратили из академика в хакера ... поэтому я бы сказал, что есть более простые способы ... хотя, возможно, не так "утонченно" академически :)

Я попытаюсь предложить альтернативный подход, с которым некоторые могут соглашаться или не соглашаться, но он МОЖЕТ быть очень практичным в рабочей среде.

Я мог бы;

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

оттуда я бы использовал классы для типов данных. разделение компонентов и разделителей элементов и итерация по возвращенным массивам.

Для меня это повторное использование кода, ОО, низкая когезия и высокая модульность ... и его легко отлаживать и программировать. проще, тем лучше.

для анализа файла вам не нужны конечные автоматы или что-то совершенно сложное. конечные автоматы хорошо подходят для анализа кода, вы будете удивлены тем, насколько мощным может быть приведенный выше псевдо-код при использовании в контексте ОО.

пс. я работал с очень похожими файлами раньше :)


Более псевдокод размещен здесь:

учебный класс

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();

и скажем, у вас есть более одного сегмента .. используйте очередь, чтобы добавить их и получить первый, второй и т. д., как вам нужно. Вы на самом деле просто представляете msg в obj и предоставляете объектные методы для вызова данных. Вы можете воспользоваться этим, также создавая собственные методы ... для наследования ... ну, это другой вопрос, и я думаю, что вы могли бы легко применить его, если вы понимаете это

Росс
источник
3
Я делал это раньше и обнаружил, что этого недостаточно для чего-либо, кроме одного или двух случаев recognize X token and do Y. Нет контекста, вы не можете иметь несколько состояний, переходя через тривиальное число случаев, вы попадаете в код, и обработка ошибок затруднена. Я считаю, что мне нужны эти функции в реальном мире почти во всех случаях. Это оставляет в стороне ошибки по мере роста сложности. Самая сложная часть - это установка скелета и изучение работы инструмента. Преодолеть это, и это так же быстро, чтобы что-то взбодрить.
Спенсер Рэтбун
это сообщение, какие штаты вам нужны? Казалось бы, такое сообщение, организованное в виде структуры из композитов и сегментов, идеально подходит для этого ОО-подхода. обработка ошибок выполняется для каждого класса и выполняется правильно, вы можете создать очень эффективный и расширяемый синтаксический анализатор. такие сообщения пригодны для классов и функций, особенно когда несколько поставщиков отправляют разные варианты одного и того же формата. Примером может служить функция в классе UNA, которая возвращает определенное значение для конкретного поставщика.
Росс
@Ross поэтому в основном вы будете иметь «UNA класс» для сегмента «УНА» и внутри него будет синтаксического анализа метод для каждого поставщика ( parseUNAsegemntForVendor1(), parseUNAsegemntForVendor2(), parseUNAsegemntForVendor3(), ... и т.д.), не так ли?
Сонго
2
@Ross В сообщении есть разделы, действительные в разных точках во время синтаксического анализа. Это те состояния, о которых я говорил. ОО дизайн умный, и я не говорю, что он не будет работать . Я придерживаюсь гибкости и бизона, потому что, как и концепции функционального программирования, они подходят лучше, чем другие инструменты, но большинство людей считают, что они слишком сложны, чтобы мешать обучению.
Спенсер Рэтбун
@ Сонго .. нет, вы бы разобрались независимо от поставщика (если вы не новичок кто). Разбор будет в INIT класса. Вы превращаете свое сообщение в объект данных на основе тех же правил, которые использовались для его создания. Однако, если вам нужно было что-то извлечь из сообщения ... и оно представлено по-разному среди ваших поставщиков, тогда у вас будут разные функции, да. Но почему это так? использовать базовый класс и иметь отдельный класс для каждого поставщика, переопределяя только при необходимости, намного проще. воспользоваться наследством.
Росс
1

Вы пробовали поискать в Google "EDIFACT PHP"? Это один из первых всплывающих результатов: http://code.google.com/p/edieasy/

Хотя этого может быть недостаточно для вашего варианта использования, вы можете получить некоторые идеи из него. Мне не нравится код с множеством вложенных циклов и условий, но это может быть началом.

chiborg
источник
1
Я проверил много проектов там, но проблема была главным образом в различных реализациях поставщиков, использующих стандарт. Я мог бы заставить одного поставщика отправить мне определенный сегмент, но я могу счесть его необязательным для другого поставщика. Вот почему мне, вероятно, все равно понадобится создать свой собственный настраиваемый парсер.
Сонго
1

Ну, с тех пор как упоминались Yacc / Bison + Flex / Lex, я мог бы также добавить одну из других основных альтернатив: комбинаторы синтаксического анализа. Они популярны в функциональном программировании, как, например, в Haskell, но если вы можете взаимодействовать с кодом на C, вы можете использовать их, и, как вы знаете, кто-то написал также для PHP. (У меня нет опыта работы с этой конкретной реализацией, но если она работает, как и большинство из них, это должно быть довольно неплохо.)

Общая концепция заключается в том, что вы начинаете с набора небольших, легко определяемых парсеров, обычно токенизаторов. Как будто у вас есть одна функция парсера для каждого из 6 элементов данных, которые вы упомянули. Затем вы используете комбинаторы (функции, которые объединяют функции) для создания больших парсеров, которые захватывают большие элементы. Как необязательный сегмент будет optionalкомбинатор, работающий на синтаксическом анализаторе сегмента.

Не уверен, насколько хорошо он работает в PHP, но это интересный способ написания парсера, и мне очень нравится использовать их на других языках.

CodexArcanum
источник
0

вместо того, чтобы возиться с регулярными выражениями, сделайте свой собственный конечный автомат

это будет более читабельным (и сможет иметь лучшие комментарии) в нетривиальных ситуациях и будет легче отлаживать, чем черный ящик, который является регулярным выражением

чокнутый урод
источник
5
Небольшое замечание, это то, что гибкость и бизон делают под капотом. Только они делают это правильно .
Спенсер Рэтбун,
0

Я не знаю, что вы хотите делать с этими данными потом, и если это не кувалда, но у меня был хороший опыт работы с eli . Вы описываете лексические фразы, а затем конкретный / абстрактный синтаксис и генерируете то, что хотите генерировать.

Себастьян Бауэр
источник