เราขอนำเสนอคำแปลคำแนะนำสั้นๆ เกี่ยวกับนิพจน์ทั่วไปในภาษา Java ซึ่งเขียนโดย Jeff Friesen สำหรับเว็บไซต์ javaworld เพื่อความสะดวกในการอ่าน เราได้แบ่งบทความออกเป็นหลายส่วน ส่วนนี้เป็นส่วนสุดท้าย
นิพจน์ทั่วไปใน Java ส่วนที่ 1 นิพจน์ทั่วไปใน Java ส่วนที่ 2 นิพจน์ทั่วไปใน Java ส่วนที่ 3 นิพจน์ทั่วไปใน Java ส่วนที่ 4
นิพจน์ทั่วไปมีประสิทธิภาพมากกว่าตัววิเคราะห์คำศัพท์ตามสถานะอย่างมาก ซึ่งต้องเขียนด้วยมือ และโดยทั่วไปแล้วจะไม่สามารถนำมาใช้ซ้ำได้ ตัวอย่างของตัววิเคราะห์คำศัพท์ที่ใช้นิพจน์ทั่วไปคือJLexซึ่งเป็นตัวสร้างคำศัพท์สำหรับภาษา Java ที่ใช้นิพจน์ทั่วไปเพื่อกำหนดกฎสำหรับการแยกสตรีมข้อมูลอินพุตออกเป็นโทเค็น อีกตัวอย่างหนึ่งคือ Lexan
ชั้นเรียน
ตำแหน่งของค่าคงที่
การใช้นิพจน์ทั่วไปสำหรับการวิเคราะห์คำศัพท์
การใช้งานนิพจน์ทั่วไปที่มีประโยชน์ยิ่งกว่านั้นก็คือไลบรารีของโค้ดที่นำมาใช้ซ้ำได้สำหรับการวิเคราะห์คำศัพท์ ซึ่งเป็นองค์ประกอบสำคัญของคอมไพเลอร์หรือแอสเซมเบลอร์ ในกรณีนี้ สตรีมอินพุตของอักขระจะถูกจัดกลุ่มเป็นสตรีมเอาต์พุตของโทเค็น - ชื่อของลำดับของอักขระที่มีความหมายร่วมกัน ตัวอย่างเช่น เมื่อพบลำดับของอักขระc
, o
, u
, n
, t
, e
, r
, ในสตรีมอินพุต ตัววิเคราะห์คำศัพท์สามารถส่งออกโทเค็นID
(ตัวระบุ) ได้ ลำดับของอักขระที่สอดคล้องกับโทเค็นเรียกว่า lexeme
ข้อมูลเพิ่มเติมเกี่ยวกับเครื่องหมายและคำศัพท์ |
---|
โทเค็น เช่น ID สามารถจับคู่ลำดับอักขระได้หลายตัว ในกรณีของโทเค็นดังกล่าว โทเค็นจริงที่สอดคล้องกับโทเค็นก็จำเป็นเช่นกันโดยคอมไพเลอร์ แอสเซมเบลอร์ หรือยูทิลิตี้อื่น ๆ ที่ต้องมีการวิเคราะห์คำศัพท์ สำหรับโทเค็นที่แสดงถึงลำดับอักขระเฉพาะชุดหนึ่ง เช่น โทเค็นPLUS ที่สอดคล้องกับอักขระเท่านั้น+ ไม่จำเป็นต้องมีโทเค็นจริง เนื่องจากโทเค็นสามารถกำหนดได้ [เฉพาะ] โดยโทเค็น |
ทำความรู้จักกับเล็กซาน
Lexan เป็นไลบรารี Java ที่สามารถนำมาใช้ซ้ำได้สำหรับการวิเคราะห์คำศัพท์ ขึ้นอยู่กับโค้ดจากชุดโพสต์ในบล็อกการเขียน Parser ใน Java บนเว็บไซต์Cogito Learning ไลบรารีประกอบด้วยคลาสต่อไปนี้ ซึ่งอยู่ในแพ็คเกจที่ca.javajeff.lexan
รวมอยู่ในโค้ดที่ดาวน์โหลดได้สำหรับบทความนี้:
Lexan
: เครื่องวิเคราะห์คำศัพท์;LexException
: มีข้อยกเว้นเกิดขึ้นหากตรวจพบไวยากรณ์ที่ไม่ถูกต้องในระหว่างการวิเคราะห์คำศัพท์Token
: ชื่อที่มีคุณลักษณะนิพจน์ทั่วไปTokLex
: คู่โทเค็น/โทเค็น
LexanException
: มีข้อยกเว้นเกิดขึ้นในตัวสร้างคลาสLexan;
Lexan(java.lang.Class tokensClass)
สร้างตัววิเคราะห์คำศัพท์ใหม่ ต้องมีหนึ่งอาร์กิวเมนต์ในรูปแบบของวัตถุคลาสjava.lang.Class
ที่สอดคล้องกับประเภทคงที่static Token
class การใช้ Reflection API ตัวสร้างจะอ่านค่าคงที่ทั้งหมดToken
ลงในอาร์เรย์ของToken[]
ค่า หากToken
ไม่มีค่าคงที่ จะมีข้อยกเว้นเกิดLexanException
ขึ้น 
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()
Lexan
getToken()
TokLex
getLexeme()
LexanException
LexException
MathTokens
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
แต่ละรายการจะได้ รับการกำหนดค่าของวัตถุ ตัวสร้างสำหรับวัตถุนี้ได้รับสตริงที่เป็นชื่อของเครื่องหมาย พร้อมด้วยนิพจน์ทั่วไปที่อธิบายสตริงอักขระทั้งหมดที่เกี่ยวข้องกับเครื่องหมายนั้น เพื่อความชัดเจน ขอแนะนำให้ชื่อสตริงของมาร์กเกอร์เหมือนกับชื่อของค่าคงที่ แต่ไม่จำเป็น 
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 เพื่อคอมไพล์โค้ด 
บทสรุป
นิพจน์ทั่วไปเป็นเครื่องมือที่มีประโยชน์ซึ่งมีประโยชน์สำหรับนักพัฒนาทุกคน Regex API ของภาษาการเขียนโปรแกรม Java ทำให้ง่ายต่อการใช้งานในแอปพลิเคชันและไลบรารี ตอนนี้คุณมีความเข้าใจพื้นฐานเกี่ยวกับนิพจน์ทั่วไปและ API นี้แล้ว ลองดูเอกสาร SDKjava.util.regex
เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับนิพจน์ทั่วไปและวิธีการ Regex API เพิ่มเติม
GO TO FULL VERSION