วิธีที่ดีที่สุดในการแยกวิเคราะห์ไฟล์


9

ฉันพยายามที่จะหาทางออกที่ดีสำหรับการทำ parser ให้บางส่วนของรูปแบบไฟล์ที่มีชื่อเสียงออกมีเช่น: EDIFACTและTRADACOMS

หากคุณไม่คุ้นเคยกับมาตรฐานเหล่านี้ลองดูตัวอย่างนี้จาก 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)ฉันสงสัยว่าการใช้ lexer / parser จะใช้งานได้หรือไม่ ถ้าเป็นฉันฉันจะลองใช้ parser combinator ก่อน :)
Guy Coder

คำตอบ:


18

สิ่งที่คุณต้องการคือเครื่องมือแยกวิเคราะห์ที่แท้จริง นิพจน์ทั่วไปจัดการกับ lexing ไม่ใช่การวิเคราะห์คำ นั่นคือพวกเขาระบุโทเค็นภายในสตรีมอินพุตของคุณ การแยกวิเคราะห์เป็นบริบทของโทเค็น IE ที่ไปที่ไหนและในลำดับใด

เครื่องมือแยกคลาสสิกเป็นyacc / กระทิง lexer คลาสสิกแนนเชี่ยล / เฟล็กซ์ เนื่องจาก php อนุญาตให้รวมรหัส Cคุณสามารถใช้ flex และ bison เพื่อสร้าง parser ของคุณให้ php เรียกมันในไฟล์อินพุต / สตรีมจากนั้นรับผลลัพธ์ของคุณ

มันจะเป็นอย่างเห็นได้ชัดและไกลง่ายต่อการทำงานร่วมกับเมื่อคุณเข้าใจเครื่องมือ ฉันแนะนำให้อ่านLex และ Yacc 2nd Ed จาก O'Reilly ตัวอย่างเช่นฉันได้ตั้งค่าโครงการแบบยืดหยุ่นและแบบกระทิงบน githubด้วย makefile มันเป็นข้าม compilable สำหรับ windows ถ้าจำเป็น

มันเป็นที่ซับซ้อน แต่เป็นคุณพบสิ่งที่คุณต้องทำคือการที่ซับซ้อน มี "สิ่งของ" มากมายที่ต้องทำเพื่อให้โปรแกรมแยกวิเคราะห์ทำงานได้อย่างถูกต้องและยืดหยุ่นและจัดการกับบิตเชิงกล มิฉะนั้นคุณจะพบว่าตัวเองอยู่ในตำแหน่งที่ไม่สามารถยอมรับได้ของการเขียนรหัสที่ชั้นนามธรรมเช่นเดียวกับการชุมนุม


1
+1 คำตอบที่ยอดเยี่ยมโดยเฉพาะอย่างยิ่งเมื่อพิจารณาว่ามาพร้อมกับตัวแยกวิเคราะห์ตัวอย่าง
Caleb

@caleb ขอบคุณฉันทำงานกับ flex / bison มาก แต่มีตัวอย่างที่ดี (อ่าน: ซับซ้อน) น้อยมาก นี่ไม่ใช่ตัวแยกวิเคราะห์ที่ดีที่สุดเนื่องจากมีความคิดเห็นไม่มากนักดังนั้นอย่าลังเลที่จะส่งอัปเดต
Spencer Rathbun

@SpencerRathbun ขอบคุณมากสำหรับคำตอบและตัวอย่างที่ละเอียด ฉันไม่มีความรู้อะไรเลยเกี่ยวกับคำศัพท์ที่คุณพูดถึง (yacc / bison, lex / flex, ... ฯลฯ ) เนื่องจากฉันมีประสบการณ์เกี่ยวกับการพัฒนาเว็บไซต์เป็นหลัก คือ"ไฟแนนเชี่และ Yacc 2nd ed"เพียงพอสำหรับผมที่จะเข้าใจทุกอย่างและสร้าง parser ดีหรือไม่? หรือมีหัวข้อและวัสดุอื่น ๆ ที่ฉันควรครอบคลุมก่อน?
Songo

@songo หนังสือเล่มนี้ครอบคลุมรายละเอียดที่เกี่ยวข้องทั้งหมดและค่อนข้างสั้นโดยมีจำนวนการตอกบัตรที่ 300 หน้าขนาดกลาง มันไม่ครอบคลุมการใช้คหรือการออกแบบภาษา โชคดีที่มีการอ้างอิง c จำนวนมากเช่นK&R ภาษาการเขียนโปรแกรม Cและคุณไม่จำเป็นต้องออกแบบภาษาเพียงทำตามมาตรฐานที่คุณอ้างอิง โปรดทราบว่าแนะนำให้อ่านปกเพื่อปกปิดเนื่องจากผู้เขียนจะพูดถึงบางสิ่งบางอย่างครั้งเดียวและสมมติว่าคุณต้องการมันคุณจะกลับไปอ่านซ้ำ ด้วยวิธีนี้คุณจะไม่พลาดอะไรเลย
Spencer Rathbun

ฉันไม่คิดว่า lexer มาตรฐานสามารถรองรับตัวคั่นแบบไดนามิกซึ่งอาจระบุบรรทัดของ UNA อย่างน้อยที่สุดคุณจะต้องใช้ lexer ที่มีอักขระที่ปรับแต่งได้เองสำหรับตัวคั่น 5 ตัว
เควิน

3

ouch .. ตัวแยกวิเคราะห์ 'จริง' เครื่องรัฐ?

ขอโทษ แต่ฉันได้รับการดัดแปลงจากนักวิชาการเป็นแฮ็กเกอร์ตั้งแต่ฉันเริ่มงานของฉัน .. ดังนั้นฉันจะบอกว่ามีวิธีที่ง่ายขึ้น .. แม้ว่าอาจจะไม่ได้ 'กลั่น' เชิงวิชาการ :)

ฉันจะพยายามเสนอวิธีการอื่นที่บางคนอาจเห็นด้วยหรืออาจไม่เห็นด้วย แต่ก็สามารถนำไปปฏิบัติได้จริงในสภาพแวดล้อมการทำงาน

ฉันจะ;

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

จากนั้นฉันจะใช้คลาสสำหรับประเภทข้อมูล แยกส่วนประกอบและองค์ประกอบแยกและทำซ้ำมากกว่าอาร์เรย์ที่ส่งคืน

สำหรับฉันนี่คือการใช้รหัสซ้ำ OO การติดต่อกันต่ำและโมดูลาร์สูง .. และง่ายต่อการดีบักและโปรแกรม ง่ายกว่าดีกว่า

ในการแยกวิเคราะห์ไฟล์ที่คุณไม่จำเป็นต้องใช้เครื่องรัฐหรืออะไรก็ตามที่ซับซ้อน .. เครื่องของรัฐนั้นเหมาะที่จะทำการแยกวิเคราะห์โค้ดคุณจะประหลาดใจที่วิธีที่มีประสิทธิภาพรหัส pseduo ข้างต้นสามารถใช้ได้เมื่อใช้ในบริบท OO

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

และบอกว่าคุณมีมากกว่าหนึ่งเซกเมนต์ .. ใช้คิวเพื่อเพิ่มและรับคิวแรก, วินาทีและอื่น ๆ ตามที่คุณต้องการ คุณเพียงแค่แสดงถึง msg ใน obj และให้วิธีการของวัตถุเพื่อเรียกข้อมูล คุณสามารถใช้ประโยชน์จากสิ่งนี้ได้โดยการสร้างวิธีการที่กำหนดเอง .. สำหรับการสืบทอด .. นั่นเป็นคำถามที่แตกต่างและฉันคิดว่าคุณสามารถนำไปใช้ได้อย่างง่ายดายหากคุณเข้าใจ


3
recognize X token and do Yผมเคยทำแบบนั้นมาก่อนและพบว่ามันไม่เพียงพอสำหรับสิ่งที่เกินกว่าหนึ่งหรือสองกรณี ไม่มีบริบทคุณไม่สามารถมีหลายสถานะย้ายผ่านจำนวนกรณีเล็ก ๆ น้อย ๆ bloats รหัสและการจัดการข้อผิดพลาดเป็นเรื่องยาก ฉันพบว่าฉันต้องการคุณสมบัติเหล่านี้ในโลกแห่งความเป็นจริงในเกือบทุกกรณี นั่นละทิ้งความผิดพลาดในมันเมื่อความซับซ้อนเพิ่มขึ้น ส่วนที่ยากที่สุดคือการติดตั้งโครงกระดูกและเรียนรู้วิธีการใช้งานเครื่องมือ ผ่านพ้นไปและมันก็เร็วพอที่จะทำอะไรบางอย่าง
Spencer Rathbun

มันเป็นข้อความคุณต้องการรัฐอะไร? ดูเหมือนว่าข้อความดังกล่าวซึ่งมีการจัดระเบียบในโครงสร้างของคอมโพสิตและส่วนจะพอดีกับวิธีการ OO นี้อย่างสมบูรณ์แบบ การจัดการข้อผิดพลาดจะทำต่อคลาสและทำได้อย่างถูกต้องคุณสามารถสร้าง parser ที่มีประสิทธิภาพมากและขยายได้ ข้อความเช่นนี้ให้ยืมตัวเองเพื่อเรียนและฟังก์ชั่นโดยเฉพาะอย่างยิ่งเมื่อผู้ขายหลายรายส่งรสชาติที่แตกต่างของรูปแบบเดียวกัน ตัวอย่างจะเป็นฟังก์ชันในคลาส UNA ซึ่งส่งคืนค่าเฉพาะสำหรับผู้ขายเฉพาะ
Ross

@Ross ดังนั้นโดยทั่วไปคุณจะมี"ชั้น UNA"สำหรับส่วนที่"อูนา"และภายในนั้นจะมีวิธีการแยกสำหรับแต่ละผู้ขาย ( parseUNAsegemntForVendor1(), parseUNAsegemntForVendor2(), parseUNAsegemntForVendor3()... ฯลฯ ) ใช่มั้ย?
Songo

2
@Ross มีบางส่วนของข้อความที่ถูกต้องที่จุดที่แตกต่างกันในระหว่างการแยก นี่คือสถานะที่ฉันพูดถึง การออกแบบ OO เป็นคนฉลาดและฉันไม่ได้บอกว่ามันจะไม่ทำงาน ฉันกดเฟล็กโซและกระทิงเพราะเช่นเดียวกับแนวคิดการเขียนโปรแกรมใช้งานได้ดีกว่าเครื่องมืออื่น ๆ แต่คนส่วนใหญ่เชื่อว่าซับซ้อนเกินไปที่จะรบกวนการเรียนรู้
Spencer Rathbun

@Songo .. ไม่คุณจะแยกเป็นอิสระจากผู้ขาย (ยกเว้นว่าคุณใหม่ใคร) การแยกจะอยู่ใน INIT ของชั้นเรียน คุณเปลี่ยนข้อความของคุณให้เป็นวัตถุข้อมูลตามกฎเดียวกับที่ใช้ในการสร้างข้อความ หากคุณต้องการที่จะคว้าบางสิ่งบางอย่างจากข้อความอย่างไรก็ตาม .. และมันมีการแสดงที่แตกต่างกันในผู้ขายของคุณแล้วคุณจะมีฟังก์ชั่นที่แตกต่างกันใช่ แต่ทำไมมันเป็นอย่างนั้น? ใช้คลาสฐานและมีคลาสแยกต่างหากสำหรับผู้ขายแต่ละรายแทนที่เฉพาะเมื่อจำเป็นเท่านั้นง่ายกว่ามาก ใช้ประโยชน์จากมรดก
Ross

1

คุณลองใช้ Google เป็น "PHP EDIFACT" แล้วหรือยัง? นี่เป็นหนึ่งในผลลัพธ์แรกที่โผล่ขึ้นมา: http://code.google.com/p/edieasy/

แม้ว่ามันอาจจะไม่เพียงพอสำหรับกรณีการใช้งานของคุณ แต่คุณอาจได้รับแนวคิดบางอย่างจากมัน ฉันไม่ชอบรหัสที่ซ้อนกันหลายครั้งสำหรับลูปและเงื่อนไข แต่อาจเป็นการเริ่มต้น


1
ฉันตรวจสอบหลายโครงการที่นั่น แต่ปัญหาส่วนใหญ่เกิดจากการใช้งานที่แตกต่างกันของผู้ขายโดยใช้มาตรฐาน ฉันอาจบังคับให้ผู้ขายรายหนึ่งส่งเซ็กเมนต์ที่แน่นอนให้ฉัน แต่ฉันอาจพิจารณาว่าเป็นทางเลือกสำหรับผู้ขายรายอื่น นั่นเป็นเหตุผลที่ฉันอาจจะต้องสร้างโปรแกรมแยกวิเคราะห์ที่กำหนดเองของฉันเอง
Songo

1

ตั้งแต่ Yacc / Bison + Flex / Lex ถูกกล่าวถึงฉันก็อาจจะโยนหนึ่งในตัวเลือกที่สำคัญอื่น ๆ : combinators parser เหล่านี้เป็นที่นิยมในการเขียนโปรแกรมการทำงานเช่นเดียวกับ Haskell แต่ถ้าคุณสามารถติดต่อรหัส C คุณสามารถใช้พวกเขาและสิ่งที่คุณรู้ว่าใครสักคนหนึ่งเขียนสำหรับ PHP เกินไป (ฉันไม่มีประสบการณ์กับการนำไปใช้เฉพาะนั้น แต่ถ้ามันใช้งานได้เหมือนส่วนใหญ่มันน่าจะดีทีเดียว)

แนวคิดทั่วไปคือคุณเริ่มต้นด้วยชุดของตัวแยกวิเคราะห์ขนาดเล็กและง่ายต่อการกำหนดโทเค็นปกติ เช่นเดียวกับคุณมีฟังก์ชั่นวิเคราะห์คำหนึ่งฟังก์ชันสำหรับองค์ประกอบข้อมูลทั้ง 6 รายการที่คุณกล่าวถึง จากนั้นคุณใช้คอมบิเนเตอร์ (ฟังก์ชั่นที่รวมฟังก์ชั่น) เพื่อสร้างตัวแยกวิเคราะห์ที่ใหญ่ขึ้น เช่นเดียวกับส่วนเพิ่มเติมที่เลือกได้คือoptionalCombinator ที่ทำงานบนตัวแยกวิเคราะห์ส่วน

ไม่แน่ใจว่ามันใช้งานได้ดีใน PHP แต่มันเป็นวิธีที่สนุกในการเขียนโปรแกรมแยกวิเคราะห์และฉันสนุกมากที่ใช้มันในภาษาอื่น


0

แทนที่จะเล่นซอกับ regexes สร้างเครื่องสถานะของคุณเอง

สิ่งนี้จะสามารถอ่านได้มากขึ้น (และสามารถมีความคิดเห็นที่ดีขึ้น) ในสถานการณ์ที่ไม่สำคัญและจะง่ายต่อการตรวจแก้จุดบกพร่องว่ากล่องดำที่เป็น regex


5
หมายเหตุสั้น ๆ นี่คือสิ่งที่งอและวัวกระทิงทำใต้ฝากระโปรง พวกเขาทำถูกต้องเท่านั้น
Spencer Rathbun

0

ผมไม่ทราบว่าสิ่งที่คุณต้องการว่าทำอะไรกับข้อมูลนี้และหลังจากนั้นถ้ามันไม่ได้เป็นค้อนขนาดใหญ่สำหรับถั่ว แต่ผมมีประสบการณ์ที่ดีกับeli คุณอธิบายวลีศัพท์จากนั้นไวยากรณ์ที่เป็นรูปธรรม / นามธรรมและสร้างสิ่งที่คุณต้องการสร้าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.