Cách tốt nhất để phân tích một tập tin


9

Tôi đang cố gắng tìm một giải pháp tốt hơn để tạo một trình phân tích cú pháp cho một số định dạng tệp nổi tiếng ngoài kia, chẳng hạn như: EDIFACTTradACOMS .

Nếu bạn không quen thuộc với các tiêu chuẩn này thì hãy xem ví dụ này từ Wikipedia:

Xem bên dưới để biết ví dụ về thông báo EDIFACT được sử dụng để trả lời yêu cầu về tính sẵn có của sản phẩm: -

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'

Phân khúc UNA là tùy chọn. Nếu có, nó chỉ định các ký tự đặc biệt sẽ được sử dụng để giải thích phần còn lại của tin nhắn. Có sáu nhân vật theo UNA theo thứ tự này:

  • phân tách thành phần dữ liệu thành phần (: trong mẫu này)
  • dấu tách phần tử dữ liệu (+ trong mẫu này)
  • thông báo thập phân (. trong mẫu này)
  • phát hành ký tự (? trong mẫu này)
  • dành riêng, phải là một không gian
  • terminator phân đoạn ('trong mẫu này)

Như bạn có thể thấy đó chỉ là một số dữ liệu được định dạng theo cách đặc biệt đang chờ phân tích cú pháp (giống như các tệp XML ).

Bây giờ hệ thống của tôi được xây dựng trên PHP và tôi đã có thể tạo trình phân tích cú pháp bằng cách sử dụng các biểu thức thông thường cho từng phân đoạn, nhưng vấn đề không phải là mọi người đều thực hiện tiêu chuẩn một cách hoàn hảo.

Một số nhà cung cấp có xu hướng bỏ qua các phân khúc và lĩnh vực tùy chọn hoàn toàn. Những người khác có thể chọn gửi nhiều dữ liệu hơn những người khác. Đó là lý do tại sao tôi buộc phải tạo trình xác nhận cho các phân đoạn và trường để kiểm tra xem tệp có đúng hay không.

Bạn có thể tưởng tượng cơn ác mộng của những biểu hiện thông thường mà tôi đang có ngay bây giờ. Ngoài ra, mỗi nhà cung cấp cần nhiều sửa đổi cho các biểu thức thông thường mà tôi có xu hướng xây dựng trình phân tích cú pháp cho mỗi nhà cung cấp.


Câu hỏi:

1- Đây có phải là cách thực hành tốt nhất để phân tích tệp (sử dụng biểu thức chính quy)?

2- Có giải pháp nào tốt hơn để phân tích tệp (có thể có giải pháp đã sẵn sàng ngoài đó) không? Nó sẽ có thể hiển thị đoạn nào bị thiếu hoặc nếu tập tin bị hỏng?

3- Nếu tôi phải xây dựng trình phân tích cú pháp của mình thì tôi nên sử dụng mẫu thiết kế hoặc phương pháp nào?

Ghi chú:

Tôi đã đọc ở đâu đó về yacc và ANTLR, nhưng tôi không biết liệu chúng có phù hợp với nhu cầu của tôi hay không!


Sau khi thấy ngữ pháp EDIFACT này , các trình phân tích cú pháp và thư viện (Java), tôi tự hỏi liệu sử dụng một lexer / trình phân tích cú pháp có hoạt động không. Nếu là tôi, tôi sẽ thử trình kết hợp trình phân tích cú pháp trước. :)
Guy Coder

Câu trả lời:


18

Những gì bạn cần là một trình phân tích cú pháp thực sự. Biểu thức thông thường xử lý từ vựng, không phân tích cú pháp. Đó là, họ xác định mã thông báo trong luồng đầu vào của bạn. Phân tích cú pháp là bối cảnh của các mã thông báo, IE đi đâu và theo thứ tự nào.

Công cụ phân tích cú pháp cổ điển là yacc / bison . Các lexer cổ điển là lex / flex . Vì php cho phép tích hợp mã C , bạn có thể sử dụng flex và bison để xây dựng trình phân tích cú pháp của mình, yêu cầu php gọi nó trên tệp / luồng đầu vào và sau đó nhận kết quả của bạn.

Nó sẽ rất nhanh , và dễ dàng hơn để làm việc với một khi bạn hiểu các công cụ . Tôi đề nghị đọc Lex và Yacc 2nd Ed. từ O'Reilly. Ví dụ, tôi đã thiết lập một dự án flex và bison trên github , với một tệp thực hiện. Nó có thể biên dịch chéo cho các cửa sổ nếu cần thiết.

rất phức tạp, nhưng như bạn đã tìm ra, những gì bạn cần làm rất phức tạp. Có rất nhiều "công cụ" phải được thực hiện cho một trình phân tích cú pháp hoạt động đúng, và xử lý flex và bison với các bit cơ học. Mặt khác, bạn thấy mình ở vị trí không thể tin được của việc viết mã ở cùng một lớp trừu tượng như lắp ráp.


1
+1 Câu trả lời tuyệt vời, đặc biệt là xem xét rằng nó đi kèm với trình phân tích cú pháp mẫu.
Caleb

@caleb cảm ơn, tôi làm việc với flex / bison rất nhiều, nhưng có rất ít ví dụ đàng hoàng (đọc: phức tạp). Đây không phải là trình phân tích cú pháp tốt nhất từ ​​trước đến nay, vì không có nhiều bình luận, vì vậy hãy gửi các bản cập nhật.
Spencer Rathbun

@SpencerRathbun cảm ơn rất nhiều vì câu trả lời và ví dụ chi tiết của bạn. Tôi không có kiến ​​thức gì về bất kỳ thuật ngữ nào bạn đã đề cập (yacc / bison, lex / flex, ... vv.) Vì kinh nghiệm của tôi chủ yếu là về phát triển web. Là "Lex và Yacc 2nd Ed" đủ cho tôi hiểu tất cả mọi thứ và xây dựng một phân tích cú pháp tốt? hoặc có những chủ đề và tài liệu khác mà tôi nên đề cập trước?
Songo

@songo Cuốn sách bao gồm tất cả các chi tiết có liên quan và khá ngắn, có thời lượng ~ 300 trang cỡ trung bình. Nó không bao gồm sử dụng c, hoặc thiết kế ngôn ngữ . May mắn thay, có rất nhiều tài liệu tham khảo c có sẵn, chẳng hạn như K & R Ngôn ngữ lập trình C và bạn không cần thiết kế ngôn ngữ, chỉ cần làm theo các tiêu chuẩn bạn đã tham chiếu. Xin lưu ý rằng đọc bìa để che được khuyến khích, vì các tác giả sẽ đề cập đến một cái gì đó một lần, và giả sử nếu bạn cần nó, bạn sẽ quay lại và đọc lại. Bằng cách đó bạn không bỏ lỡ bất cứ điều gì.
Spencer Rathbun

Tôi không nghĩ rằng một nhà từ vựng tiêu chuẩn có thể xử lý các dấu phân cách động, mà dòng UNA có thể chỉ định. Vì vậy, ít nhất bạn sẽ cần một bộ từ vựng với các ký tự có thể tùy chỉnh thời gian chạy cho 5 dấu phân cách.
Kevin

3

ouch .. 'đúng' trình phân tích cú pháp? máy nhà nước ??

xin lỗi nhưng tôi đã được chuyển đổi từ học thuật sang hacker kể từ khi tôi bắt đầu việc làm của mình .. vì vậy tôi sẽ nói có nhiều cách dễ dàng hơn .. mặc dù có thể không phải là 'tinh chế' về mặt học thuật :)

Tôi sẽ cố gắng đưa ra một cách tiếp cận khác mà một số có thể hoặc không thể đồng ý nhưng nó có thể rất thực tế trong môi trường làm việc.

Tôi sẽ;

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

từ đó tôi sẽ sử dụng các lớp cho các kiểu dữ liệu. phân tách thành phần và phân tách phần tử và lặp qua các mảng được trả về.

Đối với tôi, đây là việc sử dụng lại mã, OO, độ gắn kết thấp và tính mô đun cao .. và dễ dàng gỡ lỗi và lập trình. đơn giản hơn là tốt hơn

để phân tích một tệp mà bạn không cần máy trạng thái hoặc bất kỳ thứ gì hoàn toàn phức tạp .. máy trạng thái rất phù hợp với mã phân tích cú pháp, bạn sẽ ngạc nhiên về cách mã pseduo trên có thể mạnh mẽ như thế nào khi được sử dụng trong ngữ cảnh OO.

ps. Tôi đã làm việc với các tập tin rất giống nhau trước đây :)


Thêm mã giả được đăng ở đây:

lớp học

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

sau đó bạn có thể sử dụng nó như thế này ..

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

và nói rằng bạn có nhiều hơn một phân đoạn .. sử dụng hàng đợi để thêm chúng và nhận thứ nhất, thứ hai, v.v. khi bạn cần. Bạn thực sự chỉ cần biểu diễn thông điệp vào một obj và đưa ra các phương thức đối tượng để gọi dữ liệu. bạn có thể tận dụng điều này bằng cách tạo các phương thức tùy chỉnh .. để kế thừa .. đó là một câu hỏi khác và tôi nghĩ bạn có thể dễ dàng áp dụng nó nếu bạn hiểu nó


3
Tôi đã làm điều đó trước đây và thấy rằng nó không đủ cho bất cứ điều gì ngoài một hoặc hai trường hợp recognize X token and do Y. Không có ngữ cảnh, bạn không thể có nhiều trạng thái, di chuyển qua một số lượng nhỏ các trường hợp phình to mã và việc xử lý lỗi rất khó khăn. Tôi thấy rằng tôi đã cần những tính năng này trong thế giới thực trong hầu hết các trường hợp. Điều đó để lại những sai lầm trong đó khi sự phức tạp tăng lên. Phần khó nhất là thiết lập bộ xương và tìm hiểu cách thức hoạt động của công cụ. Hãy vượt qua điều đó và nó cũng nhanh như vậy để đánh bại thứ gì đó.
Spencer Rathbun

đó là một tin nhắn, bạn cần những trạng thái gì? có vẻ như một thông điệp như vậy, được tổ chức theo cấu trúc của vật liệu tổng hợp và phân khúc sẽ phù hợp hoàn hảo với phương pháp OO này. xử lý lỗi được thực hiện cho mỗi lớp và được thực hiện đúng cách, bạn có thể xây dựng một trình phân tích cú pháp rất hiệu quả và có thể mở rộng. các thông điệp như thế này cho vay các lớp và chức năng, đặc biệt là khi nhiều nhà cung cấp gửi các hương vị khác nhau có cùng định dạng. Một ví dụ sẽ là một hàm trong lớp UNA trả về một giá trị cụ thể cho một nhà cung cấp cụ thể.
Ross

@Ross nên về cơ bản bạn sẽ có một "UNA lớp" đối với phân khúc "UNA" và bên trong nó sẽ có một phương pháp phân tích cú pháp cho từng nhà cung cấp ( parseUNAsegemntForVendor1(), parseUNAsegemntForVendor2(), parseUNAsegemntForVendor3(), ... vv), phải không?
Songo

2
@Ross Có các phần của tin nhắn, hợp lệ tại các điểm khác nhau trong quá trình phân tích cú pháp. Đó là những trạng thái mà tôi đang nói đến. Thiết kế OO rất thông minh và tôi không nói rằng nó sẽ không hoạt động . Tôi đẩy flex và bison vì giống như các khái niệm lập trình chức năng, chúng phù hợp hơn các công cụ khác, nhưng hầu hết mọi người tin rằng chúng quá phức tạp để làm phiền việc học.
Spencer Rathbun

@Songo .. không, bạn sẽ phân tích độc lập với nhà cung cấp (trừ khi bạn là người mới). phân tích cú pháp sẽ nằm trong INIT của lớp. Bạn biến tin nhắn của mình thành một đối tượng dữ liệu dựa trên cùng các quy tắc được sử dụng để xây dựng tin nhắn. Tuy nhiên, nếu bạn cần lấy một cái gì đó từ tin nhắn .. và nó được thể hiện khác nhau giữa các nhà cung cấp của bạn thì bạn sẽ có các chức năng khác nhau .. Nhưng tại sao lại làm như vậy? sử dụng một lớp cơ sở và có một lớp riêng cho mỗi nhà cung cấp, chỉ ghi đè khi cần thiết, dễ dàng hơn nhiều. tận dụng quyền thừa kế.
Ross

1

Bạn đã thử googling cho "PHP EDIFACT" chưa? Đây là một trong những kết quả đầu tiên xuất hiện: http://code.google.com.vn/p/edieasy/

Mặc dù nó có thể không đủ cho trường hợp sử dụng của bạn, nhưng bạn có thể có được một số ý tưởng từ nó. Tôi không thích mã với nhiều vòng lặp lồng nhau cho các điều kiện và điều kiện, nhưng nó có thể là một sự khởi đầu.


1
Tôi đã kiểm tra nhiều dự án ngoài kia, nhưng vấn đề chủ yếu là ở các triển khai khác nhau của các nhà cung cấp sử dụng tiêu chuẩn. Tôi có thể buộc một nhà cung cấp gửi cho tôi một phân khúc nhất định, nhưng tôi có thể xem xét nó là tùy chọn cho nhà cung cấp khác. Đó là lý do tại sao tôi có thể sẽ cần phải xây dựng trình phân tích cú pháp tùy chỉnh của riêng mình.
Songo

1

Kể từ khi Yacc / Bison + Flex / Lex được đề cập, tôi cũng có thể ném vào một trong những lựa chọn thay thế chính khác: tổ hợp trình phân tích cú pháp. Chúng phổ biến trong lập trình chức năng như với Haskell, nhưng nếu bạn có thể giao tiếp với mã C, bạn có thể sử dụng chúng và, bạn biết gì không, ai đó cũng đã viết một cái cho PHP. (Tôi không có kinh nghiệm với việc triển khai cụ thể đó, nhưng nếu nó hoạt động như hầu hết trong số họ, thì nó sẽ khá hay.)

Khái niệm chung là bạn bắt đầu với một bộ các trình phân tích cú pháp nhỏ, dễ xác định, thường là mã thông báo. Giống như bạn có một chức năng phân tích cú pháp cho mỗi trong số 6 yếu tố dữ liệu bạn đã đề cập. Sau đó, bạn sử dụng các tổ hợp (các hàm kết hợp các hàm) để tạo các trình phân tích cú pháp lớn hơn lấy các phần tử lớn hơn. Giống như một phân đoạn tùy chọn sẽ là bộ optionalkết hợp hoạt động trên trình phân tích cú pháp phân đoạn.

Không chắc nó hoạt động tốt như thế nào trong PHP, nhưng đó là một cách thú vị để viết một trình phân tích cú pháp và tôi rất thích sử dụng chúng trong các ngôn ngữ khác.


0

thay vì loay hoay với regexes hãy tạo ra máy trạng thái của riêng bạn

điều này sẽ dễ đọc hơn (và có thể có nhận xét tốt hơn) trong các tình huống không tầm thường và sẽ dễ dàng hơn để gỡ lỗi rằng hộp đen đó là regex


5
Một lưu ý nhanh, đây là những gì flex và bison làm dưới mui xe. Chỉ có họ làm điều đó đúng .
Spencer Rathbun

0

Tôi không biết chính xác những gì bạn muốn làm với dữ liệu này sau đó và nếu nó không phải là búa tạ cho một hạt, nhưng tôi đã có kinh nghiệm tốt với eli . Bạn mô tả các cụm từ vựng và cú pháp cụ thể / trừu tượng và tạo ra những gì bạn muốn tạo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.