JavaRush /จาวาบล็อก /Random-TH /นิพจน์ทั่วไปใน Java ตอนที่ 5

นิพจน์ทั่วไปใน Java ตอนที่ 5

เผยแพร่ในกลุ่ม
เราขอนำเสนอคำแปลคำแนะนำสั้นๆ เกี่ยวกับนิพจน์ทั่วไปในภาษา Java ซึ่งเขียนโดย Jeff Friesen สำหรับเว็บไซต์ javaworld เพื่อความสะดวกในการอ่าน เราได้แบ่งบทความออกเป็นหลายส่วน ส่วนนี้เป็นส่วนสุดท้าย นิพจน์ทั่วไปใน Java ตอนที่ 5 - 1นิพจน์ทั่วไปใน Java ส่วนที่ 1 นิพจน์ทั่วไปใน Java ส่วนที่ 2 นิพจน์ทั่วไปใน Java ส่วนที่ 3 นิพจน์ทั่วไปใน Java ส่วนที่ 4

การใช้นิพจน์ทั่วไปสำหรับการวิเคราะห์คำศัพท์

การใช้งานนิพจน์ทั่วไปที่มีประโยชน์ยิ่งกว่านั้นก็คือไลบรารีของโค้ดที่นำมาใช้ซ้ำได้สำหรับการวิเคราะห์คำศัพท์ ซึ่งเป็นองค์ประกอบสำคัญของคอมไพเลอร์หรือแอสเซมเบลอร์ ในกรณีนี้ สตรีมอินพุตของอักขระจะถูกจัดกลุ่มเป็นสตรีมเอาต์พุตของโทเค็น - ชื่อของลำดับของอักขระที่มีความหมายร่วมกัน ตัวอย่างเช่น เมื่อพบลำดับของอักขระc, o, u, n, t, e, r, ในสตรีมอินพุต ตัววิเคราะห์คำศัพท์สามารถส่งออกโทเค็นID(ตัวระบุ) ได้ ลำดับของอักขระที่สอดคล้องกับโทเค็นเรียกว่า lexeme
ข้อมูลเพิ่มเติมเกี่ยวกับเครื่องหมายและคำศัพท์
โทเค็น เช่น ID สามารถจับคู่ลำดับอักขระได้หลายตัว ในกรณีของโทเค็นดังกล่าว โทเค็นจริงที่สอดคล้องกับโทเค็นก็จำเป็นเช่นกันโดยคอมไพเลอร์ แอสเซมเบลอร์ หรือยูทิลิตี้อื่น ๆ ที่ต้องมีการวิเคราะห์คำศัพท์ สำหรับโทเค็นที่แสดงถึงลำดับอักขระเฉพาะชุดหนึ่ง เช่น โทเค็นPLUSที่สอดคล้องกับอักขระเท่านั้น+ไม่จำเป็นต้องมีโทเค็นจริง เนื่องจากโทเค็นสามารถกำหนดได้ [เฉพาะ] โดยโทเค็น
นิพจน์ทั่วไปมีประสิทธิภาพมากกว่าตัววิเคราะห์คำศัพท์ตามสถานะอย่างมาก ซึ่งต้องเขียนด้วยมือ และโดยทั่วไปแล้วจะไม่สามารถนำมาใช้ซ้ำได้ ตัวอย่างของตัววิเคราะห์คำศัพท์ที่ใช้นิพจน์ทั่วไปคือJLexซึ่งเป็นตัวสร้างคำศัพท์สำหรับภาษา Java ที่ใช้นิพจน์ทั่วไปเพื่อกำหนดกฎสำหรับการแยกสตรีมข้อมูลอินพุตออกเป็นโทเค็น อีกตัวอย่างหนึ่งคือ Lexan

ทำความรู้จักกับเล็กซาน

Lexan เป็นไลบรารี Java ที่สามารถนำมาใช้ซ้ำได้สำหรับการวิเคราะห์คำศัพท์ ขึ้นอยู่กับโค้ดจากชุดโพสต์ในบล็อกการเขียน Parser ใน Java บนเว็บไซต์Cogito Learning ไลบรารีประกอบด้วยคลาสต่อไปนี้ ซึ่งอยู่ในแพ็คเกจที่ca.javajeff.lexanรวมอยู่ในโค้ดที่ดาวน์โหลดได้สำหรับบทความนี้:
  • Lexan: เครื่องวิเคราะห์คำศัพท์;
  • LexanException: มีข้อยกเว้นเกิดขึ้นในตัวสร้างคลาสLexan;
  • LexException: มีข้อยกเว้นเกิดขึ้นหากตรวจพบไวยากรณ์ที่ไม่ถูกต้องในระหว่างการวิเคราะห์คำศัพท์
  • Token: ชื่อที่มีคุณลักษณะนิพจน์ทั่วไป
  • TokLex: คู่โทเค็น/โทเค็น
ตัวสร้างLexan(java.lang.Class tokensClass)สร้างตัววิเคราะห์คำศัพท์ใหม่ ต้องมีหนึ่งอาร์กิวเมนต์ในรูปแบบของวัตถุคลาสjava.lang.Classที่สอดคล้องกับประเภทคงที่static Tokenclass การใช้ Reflection API ตัวสร้างจะอ่านค่าคงที่ทั้งหมดTokenลงในอาร์เรย์ของToken[]ค่า หากTokenไม่มีค่าคงที่ จะมีข้อยกเว้นเกิดLexanExceptionขึ้น นิพจน์ทั่วไปใน Java ตอนที่ 5 - 2ชั้นเรียนLexanยังมีสองวิธีต่อไปนี้:
  • เมธอดส่งคืนรายการของ lexer นี้List getTokLexes() Token
  • Метод void lex(String str)TokLexทำการวิเคราะห์คำศัพท์ของสตริงอินพุต [โดยวางผลลัพธ์] ลงในรายการค่าประเภท ถ้าพบอักขระที่ไม่ตรงกับรูปแบบอาเรย์ใดๆToken[]ข้อยกเว้นจะเกิดLexExceptionขึ้น
คลาสLexanExceptionไม่มีเมธอด มันใช้เมธอดที่สืบทอดมาเพื่อส่งคืนข้อความข้อgetMessage()ยกเว้น ในทางตรงกันข้าม คลาสLexExceptionมีวิธีการดังต่อไปนี้:
  • วิธีการint getBadCharIndex()ส่งกลับตำแหน่งของอักขระที่ไม่ตรงกับรูปแบบเครื่องหมายใด ๆ
  • วิธีการString getText()ส่งคืนข้อความที่ได้รับการวิเคราะห์เมื่อมีการสร้างข้อยกเว้น
ชั้นเรียนTokenแทนที่วิธีการtoString()ส่งคืนชื่อของเครื่องหมาย นอกจากนี้ยังมีวิธีการString getPattern()ที่ส่งคืนแอตทริบิวต์นิพจน์ทั่วไปของโทเค็น คลาสTokLexจัดเตรียมวิธีการToken getToken()ที่ส่งคืนโทเค็น นอกจากนี้ยังมีวิธีString getLexeme()การส่งคืนโทเค็น ด้วย

การสาธิตห้องสมุด Lexan

เพื่อสาธิตวิธีการทำงานของห้องสมุดLexanฉันจึงเขียนแอปพลิเคLexanDemoชัน ประกอบด้วยคลาสLexanDemo, BinTokens, MathTokensและNoTokens. ซอร์สโค้ดสำหรับแอปพลิเคLexanDemoชันแสดงอยู่ในรายการที่ 2 รายการ 2 การสาธิตการใช้งานไลบรารี Lexan
import ca.javajeff.lexan.Lexan;
import ca.javajeff.lexan.LexanException;
import ca.javajeff.lexan.LexException;
import ca.javajeff.lexan.TokLex;

public final class LexanDemo
{
   public static void main(String[] args)
   {
      lex(MathTokens.class, " sin(x) * (1 + var_12) ");
      lex(BinTokens.class, " 1 0 1 0 1");
      lex(BinTokens.class, "110");
      lex(BinTokens.class, "1 20");
      lex(NoTokens.class, "");
   }

   private static void lex(Class tokensClass, String text)
   {
      try
      {
         Lexan lexan = new Lexan(tokensClass);
         lexan.lex(text);
         for (TokLex tokLex: lexan.getTokLexes())
            System.out.printf("%s: %s%n", tokLex.getToken(),
                              tokLex.getLexeme());
      }
      catch (LexanException le)
      {
         System.err.println(le.getMessage());
      }
      catch (LexException le)
      {
         System.err.println(le.getText());
         for (int i = 0; i < le.getBadCharIndex(); i++)
            System.err.print("-");
         System.err.println("^");
         System.err.println(le.getMessage());
      }
      System.out.println();
   }
}
วิธีการmain()ในรายการที่ 2 เรียกใช้โปรแกรมอรรถประโยชน์lex()เพื่อสาธิตการวิเคราะห์คำศัพท์โดยใช้ Lexan การเรียกเมธอดนี้แต่ละครั้งจะถูกส่งผ่านคลาสของโทเค็นในออบเจ็กต์Classและสตริงที่จะแยกวิเคราะห์ วิธีแรก lex()สร้างวัตถุของคลาสLexanโดยส่งวัตถุClassไปยังตัวสร้างคลาส Lexanจากนั้นมันจะเรียก เมธอด lex()คลาสLexanบนสตริงนั้น หากการวิเคราะห์คำศัพท์สำเร็จเมธอดคลาสTokLexจะถูกเรียก เพื่อส่งคืนรายการ อ็อบเจ็กต์ สำหรับแต่ละอ็อบเจ็กต์เหล่านี้ วิธี การเรียน จะถูกเรียก เพื่อส่งคืนโทเค็น และวิธีการเรียนเพื่อส่งคืนโทเค็น ค่าทั้งสองจะถูกพิมพ์ไปยังเอาต์พุตมาตรฐาน หากการวิเคราะห์คำศัพท์ล้มเหลว หนึ่งในข้อยกเว้น หรือจะถูกโยนทิ้งและจัดการตามนั้น เพื่อความกระชับ ลองพิจารณาเฉพาะคลาสที่ประกอบเป็นแอปพลิเคชันนี้ รายการ 3 แสดงซอร์สโค้ดของมัน รายการ 3. คำอธิบายชุดโทเค็นสำหรับภาษาคณิตศาสตร์ขนาดเล็กgetTokLexes()LexangetToken()TokLexgetLexeme()LexanExceptionLexExceptionMathTokens
import ca.javajeff.lexan.Token;

public final class MathTokens
{
   public final static Token FUNC = new Token("FUNC", "sin|cos|exp|ln|sqrt");
   public final static Token LPAREN = new Token("LPAREN", "\\(");
   public final static Token RPAREN = new Token("RPAREN", "\\)");
   public final static Token PLUSMIN = new Token("PLUSMIN", "[+-]");
   public final static Token TIMESDIV = new Token("TIMESDIV", "[*/]");
   public final static Token CARET = new Token("CARET", "\\^");
   public final static Token INTEGER = new Token("INTEGER", "[0-9]+");
   public final static Token ID = new Token("ID", "[a-zA-Z][a-zA-Z0-9_]*");
}
รายการ 3 แสดงให้เห็นว่าคลาสMathTokensอธิบายลำดับของค่าคงที่Tokenประเภท Tokenแต่ละรายการจะได้ รับการกำหนดค่าของวัตถุ ตัวสร้างสำหรับวัตถุนี้ได้รับสตริงที่เป็นชื่อของเครื่องหมาย พร้อมด้วยนิพจน์ทั่วไปที่อธิบายสตริงอักขระทั้งหมดที่เกี่ยวข้องกับเครื่องหมายนั้น เพื่อความชัดเจน ขอแนะนำให้ชื่อสตริงของมาร์กเกอร์เหมือนกับชื่อของค่าคงที่ แต่ไม่จำเป็น นิพจน์ทั่วไปใน Java ตอนที่ 5 - 3ตำแหน่งของค่าคงที่Tokenในรายการเครื่องหมายเป็นสิ่งสำคัญ ค่าคงที่ที่อยู่ในรายการสูงกว่าTokenจะมีความสำคัญมากกว่าค่าคงที่ที่อยู่ด้านล่าง ตัวอย่างเช่น เมื่อเผชิญหน้าLexan sinจะเลือกโทเค็นFUNCแทน IDหากเครื่องหมายIDอยู่ข้างหน้าเครื่องหมายFUNCก็จะถูกเลือก

รวบรวมและรันแอปพลิเคชัน LexanDemo

รหัสที่ดาวน์โหลดได้สำหรับบทความนี้ประกอบด้วยไฟล์เก็บถาวรlexan.zipที่มีไฟล์ทั้งหมดของการแจกจ่าย Lexan แตก ไฟล์ เก็บถาวรนี้และไปที่ไดเร็กทอรีย่อยdemosของไดเร็กทอรีราก หากคุณใช้ Windows ให้รันคำสั่งต่อไปนี้เพื่อรวบรวมไฟล์ซอร์สโค้ดของแอปพลิเคชันสาธิต:lexan
javac -cp ..\library\lexan.jar *.java
หากการคอมไพล์สำเร็จ ให้รันคำสั่งต่อไปนี้เพื่อรันแอปพลิเคชันสาธิต:
java -cp ..\library\lexan.jar;. LexanDemo
คุณควรเห็นผลลัพธ์ต่อไปนี้:
FUNC: sin
LPAREN: (
ID: x
RPAREN: )
TIMESDIV: *
LPAREN: (
INTEGER: 1
PLUSMIN: +
ID: var_12
RPAREN: )
ONE: 1
ZERO: 0
ONE: 1
ZERO: 0
ONE: 1
ONE: 1
ONE: 1
ZERO: 0
1 20
--^
Неожиданный символ во входном тексте: 20
ข้อความНеожиданный символ во входном тексте: 20เกิดขึ้นอันเป็นผลมาจากข้อยกเว้นที่ถูกส่งออกไปLexanExceptionเนื่องจากคลาสBinTokensไม่ได้ประกาศค่าคงที่Tokenที่มีค่า2เป็นนิพจน์ทั่วไป โปรดทราบว่าตัวจัดการข้อยกเว้นจะแสดงตำแหน่งของอักขระที่ไม่เหมาะสมซึ่งได้รับจากการวิเคราะห์คำศัพท์ของข้อความ ข้อความโทเค็นที่หายไปเป็นผลมาจากข้อยกเว้นที่ถูกโยนออกไปLexExceptionเนื่องจากNoTokensไม่มีการประกาศค่าคงที่ ใน Tokenคลาส

เบื้องหลัง

Lexanใช้คลาส Lexan เป็นกลไก ดูที่การใช้งานคลาสนี้ในรายการ 4 และสังเกตการมีส่วนร่วมของนิพจน์ทั่วไปในการทำให้เอ็นจิ้นสามารถนำกลับมาใช้ใหม่ได้ รายการ 4. การสร้างสถาปัตยกรรมตัววิเคราะห์คำศัพท์ตามนิพจน์ทั่วไป
package ca.javajeff.lexan;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;

/**
 *  Лексический анализатор. Этот класс можно использовать для
 *  преобразования входного потока символов в выходной поток маркеров.
 *
 *  @Author Джефф Фризен
 */

public final class Lexan
{
   private List tokLexes;

   private Token[] values;

   /**
    *  Инициализируем лексический анализатор набором an objectов Token.
    *
    *  @параметры tokensClass – an object Class класса, содержащего
    *       набор an objectов Token
    *
    *  @генерирует исключение LexanException в случае невозможности
    *       формирования an object Lexan, возможно, из-за отсутствия an objectов
    *       Token в классе
    */

   public Lexan(Class tokensClass) throws LexanException
   {
      try
      {
         tokLexes = new ArrayList<>();
         List _values = new ArrayList<>();
         Field[] fields = tokensClass.getDeclaredFields();
         for (Field field: fields)
            if (field.getType().getName().equals("ca.javajeff.lexan.Token"))
               _values.add((Token) field.get(null));
         values = _values.toArray(new Token[0]);
         if (values.length == 0)
            throw new LexanException("маркеры отсутствуют");
      }
      catch (IllegalAccessException iae)
      {
         throw new LexanException(iae.getMessage());
      }

   /**
    * Получаем список TokLex'ов этого лексического анализатора.
    *
    *  @возвращает список TokLex'ов
    */

   public List getTokLexes()
   {
      return tokLexes;
   }

   /** * Выполняет лексический анализ входной строки [с помещением * результата] в список TokLex'ов. * * @параметры str – строка, подвергаемая лексическому анализу * * @генерирует исключение LexException: во входных данных обнаружен * неожиданный символ */

   public void lex(String str) throws LexException
   {
      String s = new String(str).trim(); // удалить ведущие пробелы
      int index = (str.length() - s.length());
      tokLexes.clear();
      while (!s.equals(""))
      {
         boolean match = false;
         for (int i = 0; i < values.length; i++)
         {
            Token token = values[i];
            Matcher m = token.getPattern().matcher(s);
            if (m.find())
            {
               match = true;
               tokLexes.add(new TokLex(token, m.group().trim()));
               String t = s;
               s = m.replaceFirst("").trim(); // удалить ведущие пробелы
               index += (t.length() - s.length());
               break;
            }
         }
         if (!match)
            throw new LexException("Неожиданный символ во входном тексте: "
                                    + s, str, index);
      }
   }
}
รหัสวิธีการlex()จะขึ้นอยู่กับรหัสที่ให้ไว้ในบล็อกโพสต์"การเขียน Parser ใน Java: A Token Generator"บนเว็บไซต์ Cogito Learning อ่านโพสต์นี้เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับวิธีที่ Lexan ใช้ Regex API เพื่อคอมไพล์โค้ด นิพจน์ทั่วไปใน Java ตอนที่ 5 - 4

บทสรุป

นิพจน์ทั่วไปเป็นเครื่องมือที่มีประโยชน์ซึ่งมีประโยชน์สำหรับนักพัฒนาทุกคน Regex API ของภาษาการเขียนโปรแกรม Java ทำให้ง่ายต่อการใช้งานในแอปพลิเคชันและไลบรารี ตอนนี้คุณมีความเข้าใจพื้นฐานเกี่ยวกับนิพจน์ทั่วไปและ API นี้แล้ว ลองดูเอกสาร SDK java.util.regexเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับนิพจน์ทั่วไปและวิธีการ Regex API เพิ่มเติม
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION