อ่านนิพจน์ปกติโดยไม่สูญเสียพลังหรือไม่


77

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

สำหรับผลกรณีง่าย ๆ อาจเป็นแบบนี้ในฐานะ Java regexp:

Pattern re = Pattern.compile(
  "^\\s*(?:(?:([\\d]+)\\s*:\\s*)?(?:([\\d]+)\\s*:\\s*))?([\\d]+)(?:\\s*[.,]\\s*([0-9]+))?\\s*$"
);

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

อย่างไรก็ตามสิ่งที่ไม่จำเป็นต้องซับซ้อนมากขึ้นสำหรับ regexp ที่จะกลายเป็นสิ่งที่เขียนอย่างเดียวและแม้กระทั่งกับเอกสารขยัน (ซึ่งทุกคนแน่นอนสำหรับ regexps ที่ซับซ้อนทั้งหมดที่พวกเขาเขียน ... ) การปรับเปลี่ยน regexps กลายเป็น งานที่น่ากลัว มันอาจเป็นงานที่อันตรายมากเช่นกันหาก regexp ไม่ได้ทำการทดสอบอย่างระมัดระวัง (แต่ทุกคนแน่นอนมีการทดสอบหน่วยที่ครอบคลุมสำหรับ regexps ที่ซับซ้อนทั้งหมดทั้งในเชิงบวกและเชิงลบ ... )

เรื่องสั้นสั้น ๆมีวิธีแก้ไข / อ่านทางเลือกสำหรับการแสดงออกปกติโดยไม่สูญเสียพลังของพวกเขา? regexp ข้างต้นจะมีลักษณะอย่างไรกับวิธีการทางเลือก? ภาษาใดก็ได้ แต่การแก้ปัญหาหลายภาษาจะดีที่สุด แต่สำหรับ regexps ระดับปริญญาเป็นหลายภาษา


จากนั้นสิ่งที่ regexp ก่อนหน้าทำคือ: แยกสตริงของตัวเลขในรูปแบบ1:2:3.4จับแต่ละหมายเลขที่อนุญาตให้มีช่องว่างและ3จำเป็นเท่านั้น


2
สิ่งที่เกี่ยวข้องในดังนั้น: stackoverflow.com/a/143636/674039
Wim

24
การอ่าน / แก้ไข regexes เป็นเรื่องเล็กน้อยหากคุณรู้ว่าสิ่งที่พวกเขาควรจะจับ คุณอาจเคยได้ยินเกี่ยวกับคุณสมบัติที่ไม่ค่อยได้ใช้ของภาษาส่วนใหญ่ที่เรียกว่า "ความคิดเห็น" หากคุณไม่ได้วางไว้เหนือ regex ซับซ้อนอธิบายสิ่งที่คุณจะจ่ายราคาในภายหลัง นอกจากนี้ตรวจสอบรหัส
TC1

2
สองทางเลือกในการทำความสะอาดโดยไม่ทำให้แตกเป็นชิ้นเล็ก ๆ การปรากฏตัวหรือขาดของพวกเขาแตกต่างกันไปในแต่ละภาษา (1) การขยายบรรทัด regexes โดยที่ whitespace ใน regex ถูกละเว้น (ยกเว้นว่าหลบหนี) และมีการเพิ่มแบบฟอร์มความคิดเห็นบรรทัดเดียวดังนั้นคุณสามารถแยกมันออกเป็นส่วนย่อยแบบโลจิคัลที่มีการเยื้องการเว้นวรรคบรรทัดและข้อคิดเห็น (2) กลุ่มจับภาพที่มีชื่อซึ่งคุณสามารถตั้งชื่อให้กับแต่ละ parenthetical ซึ่งทั้งสองจะเพิ่มเอกสารประกอบตัวเองและเติมแฮชของการจับคู่ให้โดยอัตโนมัติ - ดีกว่าอาเรย์ที่ทำดัชนีด้วยตัวเลขหรือตัวแปร $ N
เบ็นลี

3
ส่วนหนึ่งของปัญหาคือภาษา regex เองและตัวเลือกในประวัติศาสตร์ที่ไม่ดีในการออกแบบที่ถูกลากไปตามสัมภาระ ในภาษาที่มีสติ, การจัดกลุ่มวงเล็บเป็นอุปกรณ์ที่ใช้ในการสร้างโครงสร้างการแยกวิเคราะห์ต้นไม้ แต่ในการดำเนินการ regex จะกลับไป Unix พวกเขามีความหมาย: การลงทะเบียนที่มีผลผูกพันกับการแข่งขัน subexpression ดังนั้นคุณต้องใช้วงเล็บที่ซับซ้อนและน่าเกลียดเพื่อให้ได้การจัดกลุ่มที่บริสุทธิ์!
Kaz

2
ไม่ใช่คำตอบที่ใช้งานได้จริง แต่มันอาจมีประโยชน์ที่จะกล่าวถึงว่าพลังของการแสดงออกปกตินั้นเหมือนกับของหุ่นยนต์ จำกัด นั่นคือ regexes สามารถตรวจสอบ / แยกวิเคราะห์คลาสเดียวกันของสายการตรวจสอบและแยกโดยอัตโนมัติ จำกัด ดังนั้นการเป็นตัวแทนของมนุษย์ที่อ่านได้ของ regex น่าจะสามารถสร้างกราฟได้อย่างรวดเร็วและฉันเชื่อว่าภาษาที่ใช้ข้อความส่วนใหญ่นั้นแย่มาก นั่นเป็นเหตุผลที่เราใช้เครื่องมือแสดงผลสำหรับสิ่งต่าง ๆ ดูที่hackingoff.com/compilers/regular-expression-to-nfa-dfaเพื่อรับแรงบันดาลใจ
damix911

คำตอบ:


80

ผู้คนจำนวนมากได้กล่าวถึงการแต่งเพลงจากส่วนเล็ก ๆ แต่ยังไม่มีใครให้ตัวอย่างดังนั้นนี่คือของฉัน:

string number = "(\\d+)";
string unit = "(?:" + number + "\\s*:\\s*)";
string optionalDecimal = "(?:\\s*[.,]\\s*" + number + ")?";

Pattern re = Pattern.compile(
  "^\\s*(?:" + unit + "?" + unit + ")?" + number + optionalDecimal + "\\s*$"
);

ไม่ใช่อ่านได้มากที่สุด แต่ฉันรู้สึกว่ามันชัดเจนกว่าต้นฉบับ

นอกจากนี้ C # มี@ผู้ประกอบการที่สามารถใช้ได้กับสตริงเพื่อที่จะแสดงให้เห็นว่ามันเป็นสิ่งที่จะต้องดำเนินการอย่างแท้จริง (ไม่มีตัวหนี) ดังนั้นnumberจะ@"([\d]+)";


เพิ่งสังเกตเห็นว่าทั้งสอง[\\d]+และ[0-9]+ควรเป็นเพียงแค่\\d+(ดีบางคนอาจพบว่า[0-9]+อ่านได้มากขึ้น) ฉันจะไม่แก้ไขคำถาม แต่คุณอาจต้องการแก้ไขคำตอบนี้
hyde

@hyde - จับได้ดี เทคนิคที่พวกเขากำลังไม่มากในสิ่งเดียวกัน - \dจะตรงกับสิ่งที่ถือว่าเป็นจำนวนมากแม้จะอยู่ในระบบเลขอื่น ๆ (จีน, อาหรับ, ฯลฯ ) ในขณะที่[0-9]ก็จะตรงกับตัวเลขมาตรฐาน \\dแม้ว่าฉันจะสร้างมาตรฐานบนและแยกเป็นoptionalDecimalรูปแบบ
Bobson

42

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

ภายในPerl/xผู้ประกอบการในตอนท้ายของการแสดงออกปกติระงับช่องว่างทำให้หนึ่งเพื่อจัดทำเอกสารการแสดงออกปกติ

การแสดงออกปกติข้างต้นจะกลายเป็น:

$re = qr/
  ^\s*
  (?:
    (?:       
      ([\d]+)\s*:\s*
    )?
    (?:
      ([\d]+)\s*:\s*
    )
  )?
  ([\d]+)
  (?:
    \s*[.,]\s*([\d]+)
  )?
  \s*$
/x;

ใช่มันใช้พื้นที่ว่างในแนวดิ่งเพียงเล็กน้อย แต่ก็สามารถทำให้สั้นลงได้โดยไม่ต้องอ่านมากเกินไป

จากนั้นสิ่งที่ regexp ก่อนหน้าทำคือ: แยกสตริงของตัวเลขในรูปแบบ 1: 2: 3.4 โดยการจับแต่ละหมายเลขที่อนุญาตให้มีช่องว่างและต้องการเพียง 3

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

วิธีการที่คล้ายกันสามารถดำเนินการในภาษาอื่น ๆ หลามre.VERBOSEตัวเลือกทำงานที่นั่น

Perl6 (ตัวอย่างข้างต้นสำหรับ perl5) ใช้เวลานี้ต่อไปกับแนวคิดของกฎที่นำไปสู่โครงสร้างที่มีประสิทธิภาพยิ่งกว่า PCRE (มันให้การเข้าถึงไวยากรณ์อื่น ๆ (บริบทที่ไวและบริบทที่ไว) กว่าปกติธรรมดาและขยาย

ใน Java (โดยที่ตัวอย่างนี้ดึงมา) หนึ่งสามารถใช้การต่อสตริงเพื่อสร้าง regex

Pattern re = Pattern.compile(
  "^\\s*"+
  "(?:"+
    "(?:"+
      "([\\d]+)\\s*:\\s*"+  // Capture group #1
    ")?"+
    "(?:"+
      "([\\d]+)\\s*:\\s*"+  // Capture group #2
    ")"+
  ")?"+ // First groups match 0 or 1 times
  "([\\d]+)"+ // Capture group #3
  "(?:\\s*[.,]\\s*([0-9]+))?"+ // Capture group #4 (0 or 1 times)
  "\\s*$"
);

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

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


13
มีความแตกต่างอย่างมากระหว่าง "การจัดทำเอกสาร" และ "การเพิ่มตัวแบ่งบรรทัด"

4
@JonofAllTrades การสร้างโค้ดที่สามารถอ่านได้เป็นขั้นตอนแรกของทุกสิ่ง การเพิ่มตัวแบ่งบรรทัดช่วยให้สามารถเพิ่มความคิดเห็นสำหรับชุดย่อยของ RE นั้นในบรรทัดเดียวกัน (สิ่งที่ยากต่อการทำในบรรทัดยาวบรรทัดเดียวของข้อความนิพจน์ทั่วไป)

2
@ JonofAllTrades ฉันไม่เห็นด้วยอย่างยิ่ง "การจัดทำเอกสาร" และ "การเพิ่มตัวแบ่งบรรทัด" นั้นไม่แตกต่างกันในการที่พวกเขาทั้งสองมีจุดประสงค์เดียวกัน - ทำให้เข้าใจรหัสได้ง่ายขึ้น และสำหรับโค้ดที่มีการจัดรูปแบบไม่ดีการ "เพิ่มตัวแบ่งบรรทัด" นั้นมีจุดประสงค์ที่ดีกว่าการเพิ่มเอกสารประกอบ
เบ็นลี

2
การเพิ่มตัวแบ่งบรรทัดเป็นการเริ่มต้น แต่มันก็ประมาณ 10% ของงาน คำตอบอื่น ๆ ให้รายละเอียดเพิ่มเติมซึ่งเป็นประโยชน์

26

โหมด "verbose" ที่นำเสนอโดยบางภาษาและไลบรารีเป็นหนึ่งในคำตอบสำหรับข้อกังวลเหล่านี้ ในโหมดนี้ช่องว่างในสตริง regexp จะถูกตัดออก (ดังนั้นคุณจำเป็นต้องใช้\s) และความคิดเห็นที่เป็นไปได้ นี่เป็นตัวอย่างสั้น ๆ ในPythonที่รองรับสิ่งนี้ตามค่าเริ่มต้น:

email_regex = re.compile(r"""
    ([\w\.\+]+) # username (captured)
    @
    \w+         # minimal viable domain part
    (?:\.w+)    # rest of the domain, after first dot
""", re.VERBOSE)

ในภาษาใด ๆ ที่ไม่ได้ใช้งานนักแปลจากโหมด verbose ถึง "ปกติ" ควรเป็นงานง่าย หากคุณกังวลเกี่ยวกับความสามารถในการอ่าน regexps ของคุณคุณอาจจะต้องพิสูจน์ว่าการลงทุนครั้งนี้ค่อนข้างง่าย


15

ทุกภาษาที่ใช้ regexes ช่วยให้คุณสามารถเรียบเรียงจากบล็อกที่เรียบง่ายขึ้นเพื่อให้อ่านง่ายขึ้นและด้วยสิ่งที่ซับซ้อนกว่า (หรือซับซ้อนกว่า) ตัวอย่างของคุณคุณควรใช้ประโยชน์จากตัวเลือกนั้นอย่างแน่นอน ปัญหาโดยเฉพาะอย่างยิ่งกับ Java และภาษาอื่น ๆ คือพวกเขาไม่ปฏิบัติต่อการแสดงออกปกติในฐานะพลเมือง "ชั้นหนึ่ง" แทนที่จะต้องการให้พวกเขาแอบเข้าไปในภาษาผ่านตัวอักษรสตริง นี่หมายถึงเครื่องหมายคำพูดและแบ็กสแลชจำนวนมากที่ไม่ได้เป็นส่วนหนึ่งของไวยากรณ์ของ regex และทำให้อ่านยากและมันก็หมายความว่าคุณไม่สามารถอ่านได้มากกว่านั้นโดยไม่ต้องกำหนดมินิภาษาและล่ามของคุณเอง

วิธีที่ดีกว่าในการรวมนิพจน์ทั่วไปคือ Perl ด้วยตัวเลือกช่องว่างและตัวดำเนินการ regex-quoting Perl 6 ขยายแนวความคิดในการสร้าง regexes จากชิ้นส่วนไปยังแกรมแกรมแบบเรียกซ้ำซึ่งดีกว่ามาก ๆ ในการใช้มันไม่มีการเปรียบเทียบเลย ภาษาอาจพลาดเรือที่ทันเวลา แต่การสนับสนุน regex ของมันคือ The Good Stuff (tm)


1
ด้วยการ "บล็อกที่เรียบง่าย" ที่กล่าวถึงในตอนต้นของคำตอบคุณหมายถึงแค่การต่อสตริงหรือสิ่งที่ก้าวหน้ากว่านี้
hyde

7
ฉันหมายถึงการกำหนดนิพจน์ย่อยเป็นตัวอักษรสตริงที่สั้นกว่ากำหนดให้กับตัวแปรท้องถิ่นที่มีชื่อที่มีความหมายแล้วต่อกัน ฉันพบว่าชื่อมีความสำคัญต่อการอ่านง่ายกว่าการปรับปรุงโครงร่าง
Kilian Foth

11

ฉันชอบใช้ Expresso: http://www.ultrapico.com/Expresso.htm

แอปพลิเคชันฟรีนี้มีคุณสมบัติต่อไปนี้ซึ่งฉันพบว่ามีประโยชน์เมื่อเวลาผ่านไป:

  • คุณสามารถคัดลอกและวาง regex ของคุณและแอปพลิเคชันจะแยกวิเคราะห์ให้คุณ
  • เมื่อเขียน regex ของคุณคุณสามารถทดสอบได้โดยตรงจากแอปพลิเคชัน (แอปพลิเคชันจะให้รายการของการจับการแทนที่ ... )
  • เมื่อคุณทดสอบแล้วมันจะสร้างรหัส C # เพื่อนำไปใช้งาน (โปรดทราบว่ารหัสจะมีคำอธิบายเกี่ยวกับ regex ของคุณ)

ตัวอย่างเช่นเมื่อคุณเพิ่งส่ง regex มันจะมีลักษณะดังนี้: หน้าจอตัวอย่างที่มี regex ที่กำหนดเริ่มต้น

แน่นอนว่าการลองใช้นั้นมีค่านับพันคำอธิบาย โปรดทราบว่าฉันทราบว่าเกี่ยวข้องกับโปรแกรมแก้ไขของโปรแกรมนี้


4
คุณจะอธิบายเกี่ยวกับเรื่องนี้ในรายละเอียดเพิ่มเติมได้อย่างไร - มันจะตอบคำถามที่ถามได้อย่างไรและทำไม? "คำตอบสำหรับลิงก์เท่านั้น"ไม่ได้รับการต้อนรับอย่างมากที่กองแลกเปลี่ยน
ริ้น

5
@gnat ขออภัยเกี่ยวกับสิ่งนั้น คุณพูดถูก ฉันหวังว่าคำตอบที่แก้ไขจะให้ข้อมูลเชิงลึกมากขึ้น
E. Jaep

9

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

BNF, EBNF และอื่น ๆ ไวยากรณ์สามารถอ่านและสร้างได้ง่ายกว่านิพจน์ทั่วไปที่ซับซ้อน ทองคำเป็นเครื่องมือหนึ่งสำหรับสิ่งเหล่านี้

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

ทางเลือกอื่นในการแสดงออกปกติ

การใช้ "ทางเลือก" เพื่อหมายถึง "สิ่งอำนวยความสะดวกที่มีความหมายเทียบเท่ากับทางไวยากรณ์ที่แตกต่างกัน" มีอย่างน้อยทางเลือกเหล่านี้เพื่อ / กับ RegularExpressions:

  • การแสดงออกปกติพื้นฐาน
  • นิพจน์ปกติ "ขยาย"
  • การแสดงออกปกติที่รองรับ Perl
  • ... และตัวแปรอื่น ๆ อีกมากมาย ...
  • ไวยากรณ์ RE สไตล์ SNOBOL (SnobolLanguage, IconLanguage)
  • ไวยากรณ์ SRE (RE เป็น EssExpressions)
  • FSM ต่างกัน
  • ไฟแกรมสี่แยกไฟไนท์รัฐ (ค่อนข้างชัดเจน)
  • ParsingExpressionGrammars ดังเช่นใน OMetaLanguage และ LuaLanguage ( http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html )
  • โหมดการแยกวิเคราะห์ของ RebolLanguage
  • ProbabilityBasedParsing ...

คุณจะอธิบายเพิ่มเติมเกี่ยวกับสิ่งที่ลิงค์นี้และสิ่งที่ดีสำหรับ? "คำตอบสำหรับลิงก์เท่านั้น"ไม่ได้รับการต้อนรับอย่างมากที่กองแลกเปลี่ยน
ริ้น

1
ยินดีต้อนรับสู่โปรแกรมเมอร์ Nick P โปรดข้าม downvote / r แต่อ่านหน้าใน meta ที่ @gnat เชื่อมโยงกับ
Christoffer Lette

@ Christoffer Lette ชื่นชมคำตอบของคุณ จะพยายามจดจำสิ่งนี้ไว้ในโพสต์ในอนาคต ความคิดเห็นของ @ gnat Paulo Scardine สะท้อนถึงเจตนาของโพสต์ของฉัน BNF, EBNF และอื่น ๆ ไวยากรณ์สามารถอ่านและสร้างได้ง่ายกว่านิพจน์ทั่วไปที่ซับซ้อน ทองคำเป็นเครื่องมือหนึ่งสำหรับสิ่งเหล่านี้ ลิงก์ c2 มีรายการตัวเลือกที่เป็นไปได้ซึ่งสามารถ googled โดยมีการพูดคุยรวมอยู่ด้วย มันเป็นลิงค์ "เห็นด้วย" ที่ด้านบนของคำแนะนำเครื่องมือไวยากรณ์ของฉัน
Nick P

6

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

// Create an example of how to test for correctly formed URLs
var tester = VerEx()
    .startOfLine()
    .then('http')
    .maybe('s')
    .then('://')
    .maybe('www.')
    .anythingBut(' ')
    .endOfLine();

// Create an example URL
var testMe = 'https://www.google.com';

// Use RegExp object's native test() function
if (tester.test(testMe)) {
    alert('We have a correct URL '); // This output will fire}
} else {
    alert('The URL is incorrect');
}

console.log(tester); // Outputs the actual expression used: /^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$/

ตัวอย่างนี้สำหรับ javascript คุณสามารถค้นหาไลบรารี่นี้สำหรับภาษาการเขียนโปรแกรมจำนวนมาก


2
นี่มันเจ๋งมาก!
Jeremy Thompson

3

วิธีที่ง่ายที่สุดคือการใช้ regex แต่สร้างนิพจน์ของคุณจากการเขียนนิพจน์ที่เรียบง่ายด้วยชื่อที่สื่อความหมายเช่นhttp://www.martinfowler.com/bliki/ComposedRegex.html (และใช่นี่มาจาก string concat)

อย่างไรก็ตามเป็นอีกทางเลือกหนึ่งที่คุณสามารถใช้ parser combinator library เช่นhttp://jparsec.codehaus.org/ซึ่งจะทำให้คุณมี parser ที่สมบูรณ์แบบ พลังที่แท้จริงที่นี่มาจากองค์ประกอบอีกครั้ง (คราวนี้องค์ประกอบการทำงาน)


3

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

รูปแบบข้างต้นที่แสดงใน grok คือ (ฉันทดสอบในแอพดีบักเกอร์แต่อาจผิดพลาดได้):

"(( *%{NUMBER:a} *:)? *%{NUMBER:b} *:)? *%{NUMBER:c} *(. *%{NUMBER:d} *)?"

ชิ้นส่วนและช่องว่างที่เป็นตัวเลือกทำให้ดูน่าเกลียดกว่าปกติ แต่ทั้งที่นี่และในกรณีอื่น ๆ การใช้ grok สามารถทำให้ชีวิตของคุณดีกว่ามาก


2

ใน F # คุณมีโมดูลFsVerbalExpressions ช่วยให้คุณสามารถเขียน Regexes จากนิพจน์ทางวาจา แต่ก็มี regexes ที่สร้างไว้ล่วงหน้าบางส่วน (เช่น URL)

หนึ่งในตัวอย่างสำหรับไวยากรณ์นี้คือ:

let groupName =  "GroupNumber"

VerbEx()
|> add "COD"
|> beginCaptureNamed groupName
|> any "0-9"
|> repeatPrevious 3
|> endCapture
|> then' "END"
|> capture "COD123END" groupName
|> printfn "%s"

// 123

หากคุณไม่คุ้นเคยกับไวยากรณ์ F # groupName คือสตริง "GroupNumber"

จากนั้นพวกเขาสร้าง Verbal Expression (VerbEx) ซึ่งสร้างเป็น "COD (? <GroupNumber> [0-9] {3}) END" ซึ่งพวกเขาทดสอบกับสตริง "COD123END" ซึ่งพวกเขาได้รับกลุ่มการจับภาพชื่อ "GroupNumber" ผลลัพธ์นี้ใน 123

ฉันพบว่า regex ปกติเข้าใจง่ายกว่ามาก


-2

ก่อนอื่นให้ทำความเข้าใจว่ารหัสที่ใช้งานได้เท่านั้นคือรหัสที่ไม่ดี รหัสที่ดียังต้องรายงานข้อผิดพลาดที่พบ

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

ทีนี้ลองคิดถึง "การวิเคราะห์สตริงของตัวเลขในรูปแบบ 1: 2: 3.4" regex ทั้งหมดทำรายงาน "ผ่าน / ล้มเหลว" ที่ไม่อนุญาตให้แสดงความคิดเห็นอย่างเพียงพอต่อผู้ใช้ (ไม่ว่าข้อเสนอแนะนี้เป็นข้อความแสดงข้อผิดพลาดในบันทึกหรือ GUI แบบโต้ตอบที่ข้อผิดพลาดจะแสดงเป็นสีแดงเป็น ประเภทผู้ใช้หรือสิ่งอื่นใด) ข้อผิดพลาดประเภทใดที่อธิบายไม่ถูกต้อง อักขระไม่ถูกต้องในหมายเลขแรก, หมายเลขแรกใหญ่เกินไป, เครื่องหมายโคลอนที่หายไปหลังหมายเลขแรก ฯลฯ

ในการแปลง "รหัสไม่ดีที่ใช้งานได้เพียง" เป็น "รหัสที่ดีซึ่งมีข้อผิดพลาดที่อธิบายอย่างพอเพียง" คุณต้องแยก regex ออกเป็น regexes ขนาดเล็กจำนวนมาก (โดยทั่วไปแล้ว regex ที่มีขนาดเล็กมาก )

การทำให้โค้ดอ่าน / บำรุงรักษาได้นั้นเป็นผลมาจากการทำให้โค้ดนั้นดี


6
อาจไม่ใช่ข้อสมมติฐานที่ดี เหมืองแร่เป็นเพราะ A) นี้ไม่ได้อยู่ที่คำถาม ( วิธีที่จะทำให้มันสามารถอ่านได้?) B) การแสดงออกปกติที่ตรงกันคือผ่าน / ไม่ผ่านและถ้าคุณทำลายมันลงไปยังจุดที่คุณสามารถพูดได้ว่าทำไมมันล้มเหลวคุณ สูญเสียพลังงานและความเร็วจำนวนมากและเพิ่มความซับซ้อน C) ไม่มีข้อบ่งชี้จากคำถามว่ามีความเป็นไปได้ที่การแข่งขันจะล้มเหลว - เป็นเพียงคำถามเกี่ยวกับการทำให้ Regex อ่านได้ เมื่อคุณมีการควบคุมข้อมูลที่เข้าและ / หรือตรวจสอบข้อมูลก่อนมือคุณสามารถถือว่าข้อมูลนั้นถูกต้อง
Bobson

A) การแตกมันเป็นชิ้นเล็ก ๆ ทำให้อ่านง่ายขึ้น (ซึ่งเป็นผลมาจากการทำให้ดี) C) ในกรณีที่สตริงที่ไม่รู้จัก / ไม่ผ่านการตรวจสอบป้อนชิ้นส่วนของซอฟต์แวร์นักพัฒนาสติจะแยกวิเคราะห์ (ที่มีการรายงานข้อผิดพลาด) ที่จุดนั้นและแปลงข้อมูลเป็นรูปแบบที่ไม่จำเป็นต้องแยกซ้ำ - regex ไม่จำเป็นหลังจากนั้น B) เป็นเรื่องไร้สาระที่ใช้เฉพาะกับรหัสที่ไม่ดี (อ้างอิงถึงจุด A และ C)
เบรนแดน

ไปจาก C ของคุณ: ถ้านี่เป็นตรรกะการตรวจสอบของเขา? รหัสของ OP อาจเป็นสิ่งที่คุณแนะนำอย่างแน่นอน - ตรวจสอบความถูกต้องของอินพุตรายงานหากไม่ถูกต้องและแปลงเป็นรูปแบบที่ใช้งานได้ (ผ่านการจับ) ทั้งหมดที่เรามีคือการแสดงออก คุณจะแนะนำให้ทำการแยกวิเคราะห์นอกเหนือจาก regex อย่างไร หากคุณเพิ่มโค้ดตัวอย่างที่จะให้ผลลัพธ์เดียวกันฉันจะลบ downvote ของฉัน
Bobson

หากนี่คือ "C: การตรวจสอบความถูกต้อง (พร้อมการรายงานข้อผิดพลาด)" ก็เป็นรหัสที่ไม่ดีเพราะการรายงานข้อผิดพลาดไม่ดี หากล้มเหลว มันเป็นเพราะสตริงถูกโมฆะหรือเพราะจำนวนแรกมีตัวเลขมากเกินไปหรือเพราะตัวคั่นแรกไม่ได้:? ลองนึกภาพคอมไพเลอร์ที่มีข้อความแสดงข้อผิดพลาดเดียว ("ข้อผิดพลาด") ที่โง่เกินกว่าจะบอกผู้ใช้ว่าปัญหาคืออะไร ตอนนี้ลองนึกภาพเว็บไซต์หลายพันแห่งที่โง่และแสดงผล (เช่น) "ที่อยู่อีเมลไม่ดี" และไม่มีอะไรเพิ่มเติมอีกแล้ว
เบรนแดน

นอกจากนี้ลองจินตนาการว่าผู้ให้บริการช่วยเหลือที่ได้รับการฝึกอบรมเพียงครึ่งเดียวได้รับรายงานข้อผิดพลาดจากผู้ใช้ที่ยังไม่ผ่านการฝึกอบรมอย่างสมบูรณ์ซึ่งกล่าวว่า: ซอฟต์แวร์หยุดทำงาน - บรรทัดสุดท้ายในบันทึกของซอฟต์แวร์คือ "ข้อผิดพลาด: ไม่สามารถแยกหมายเลขรุ่นรอง '(คาดว่าโคลอนหลังจากตัวเลขที่สอง) "
เบรนแดน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.