JavaRush /Java Blog /Random-KO /Java의 정규 표현식, 5부

Java의 정규 표현식, 5부

Random-KO 그룹에 게시되었습니다
우리는 Javaworld 웹사이트를 위해 Jeff Friesen이 작성한 Java 정규식에 대한 짧은 가이드의 번역을 여러분의 주의에 제시합니다. 읽기 쉽도록 기사를 여러 부분으로 나누었습니다. 이 부분이 마지막 부분입니다. Java의 정규 표현식, 5부 - 1Java 정규식, 1부 Java 정규식, 2부 Java 정규식, 3부 Java 정규식, 4부

어휘 분석을 위한 정규식 사용

정규식을 더욱 유용하게 적용하는 방법은 컴파일러나 어셈블러의 핵심 구성 요소인 어휘 분석을 수행하기 위한 재사용 가능한 코드 라이브러리입니다. 이 경우 문자의 입력 스트림은 토큰의 출력 스트림(공통 의미를 갖는 문자 시퀀스의 이름)으로 그룹화됩니다. 예를 들어, 입력 스트림에서 문자 시퀀스 c, o, u, n, t, e, 를 우연히 발견한 r경우 어휘 분석기는 토큰 ID(식별자)을 출력할 수 있습니다. 토큰에 해당하는 문자 시퀀스를 어휘소라고 합니다.
마커 및 어휘소에 대한 추가 정보
ID와 같은 토큰은 다양한 문자 시퀀스와 일치할 수 있습니다. 이러한 토큰의 경우 어휘 분석이 필요한 컴파일러, 어셈블러 또는 기타 유틸리티에서는 토큰에 해당하는 실제 토큰도 필요합니다. PLUS문자 에만 해당하는 토큰과 같이 하나의 특정 문자 시퀀스를 나타내는 토큰의 경우 +토큰에 의해 [고유하게] 결정될 수 있으므로 실제 토큰이 필요하지 않습니다.
정규식은 수동으로 작성해야 하고 일반적으로 재사용할 수 없는 상태 기반 어휘 분석기보다 훨씬 더 효율적입니다. 정규식 기반 어휘 분석기의 예로는 정규식을 사용하여 입력 데이터 스트림을 토큰으로 분할하기 위한 규칙을 정의하는 Java 언어용 어휘 생성기인 JLex 가 있습니다. 또 다른 예는 Lexan입니다.

Lexan 알아보기

Lexan은 어휘 분석을 위한 재사용 가능한 Java 라이브러리입니다. 이는 Cogito Learning 웹 사이트 의 블로그 게시물 시리즈 Writing a Parser in Java 의 코드를 기반으로 합니다 . 라이브러리는 이 기사의 다운로드 가능한 코드에 포함된 패키지에 있는 다음 클래스로 구성됩니다 .ca.javajeff.lexan
  • Lexan: 어휘 분석기;
  • LexanException: 클래스 생성자에서 예외가 발생했습니다.Lexan;
  • LexException: 어휘 분석 중에 잘못된 구문이 감지되면 예외가 발생합니다.
  • Token: 정규식 속성을 가진 이름;
  • TokLex: 토큰/토큰 쌍.
생성자는 Lexan(java.lang.Class tokensClass)새로운 어휘 분석기를 생성합니다. java.lang.Class여기 에는 상수 class 유형에 해당하는 클래스 객체 형식의 인수가 하나 필요합니다 static Token. Reflection API를 사용하여 생성자는 모든 상수를 Token값 배열로 읽어옵니다 Token[]. 상수가 없으면 Token예외가 발생합니다 LexanException. Java의 정규 표현식, 5부 - 2이 클래스는 Lexan다음 두 가지 메서드도 제공합니다.
  • 이 메소드는 이 어휘 분석기의 목록을 반환합니다 .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();
   }
}
목록 2의 메소드는 Lexan을 사용하여 어휘 분석을 시연하는 main()유틸리티를 호출합니다 lex(). 이 메서드에 대한 각 호출에는 개체의 토큰 클래스 Class와 구문 분석할 문자열이 전달됩니다. 이 메서드는 lex()먼저 객체를 클래스 생성자에 Lexan전달하여 Class클래스의 객체를 만듭니다 Lexan. 그런 다음 해당 문자열에 대해 lex()클래스 메서드를 호출합니다. Lexan어휘 분석이 성공하면 클래스 TokLex메소드가 호출되어 객체 목록을 반환합니다 . 이러한 각 개체에 대해 해당 클래스 메서드가 호출되어 토큰을 반환하고 해당 클래스 메서드가 토큰을 반환합니다. 두 값 모두 표준 출력으로 인쇄됩니다. 어휘 분석이 실패하면 예외 중 하나가 발생 하고 이에 따라 처리됩니다 . 간결하게 하기 위해 이 애플리케이션을 구성하는 클래스만 고려해 보겠습니다 . Listing 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_]*");
}
Listing 3에서는 클래스가 MathTokens유형의 상수 시퀀스를 설명하는 것을 보여줍니다 Token. 그들 각각에는 객체의 가치가 할당됩니다 Token. 이 개체의 생성자는 해당 마커와 관련된 모든 문자열을 설명하는 정규식과 함께 마커의 이름인 문자열을 받습니다. 명확하게 하기 위해 마커의 문자열 이름이 상수의 이름과 동일한 것이 바람직하지만 필수는 아닙니다. 마커 목록에서 Java의 정규 표현식, 5부 - 3상수의 위치가 중요합니다. Token목록에서 더 높은 곳에 있는 상수가 Token아래에 있는 상수보다 우선합니다. 예를 들어, 가 발생하면 Lexan은 대신 sin토큰을 선택합니다 . 토큰이 토큰 앞에 있었다면 선택되었을 것입니다. FUNCIDIDFUNC

LexanDemo 애플리케이션 컴파일 및 실행

lexan.zip이 기사의 다운로드 가능한 코드 에는 Lexan 배포판의 모든 파일이 포함된 아카이브가 포함되어 있습니다 . 이 아카이브의 압축을 풀고 demos루트 디렉터리의 하위 디렉터리로 이동합니다 lexan. Windows를 사용하는 경우 다음 명령을 실행하여 데모 애플리케이션 소스 코드 파일을 컴파일합니다.
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때문에 발생하는 예외의 결과로 발생합니다 . 예외 처리기는 텍스트의 어휘 분석에서 얻은 부적절한 문자의 위치를 ​​출력합니다. 토큰 누락 메시지는 클래스에 상수가 선언되지 않았기 때문에 발생하는 예외의 결과입니다 . LexanExceptionBinTokensToken2LexExceptionNoTokensToken

무대 뒤에서

LexanLexan 클래스를 엔진으로 사용합니다. 목록 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);
      }
   }
}
메소드 코드는 Cogito Learning 웹사이트의 lex()블로그 게시물 "Writing a Parser in Java: A Token Generator" 에 제공된 코드를 기반으로 합니다 . Lexan이 Regex API를 사용하여 코드를 컴파일하는 방법에 대해 자세히 알아보려면 이 게시물을 읽어보세요. Java의 정규식, 5부 - 4

결론

정규식은 모든 개발자에게 유용할 수 있는 유용한 도구입니다. Java 프로그래밍 언어의 Regex API를 사용하면 애플리케이션과 라이브러리에서 쉽게 사용할 수 있습니다. 이제 정규식과 이 API에 대한 기본 사항을 이해했으므로 SDK 설명서를 살펴보고 java.util.regex정규식과 추가 Regex API 메서드에 대해 자세히 알아보세요.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION