2017-01-06 9 views
1

私はいくつかの数式を含むテキストファイルを持っています。 正規表現を使用してテキストをコンポーネント(単語、文、句読点、数字、算術記号)に解析し、数式を計算し、計算された数式で元の形式でテキストを返す必要があります。 私はこれを正規表現なしで(計算なしで)行いました。今私は正規表現を使用してこれをしようとしています。 これを正しく行う方法を完全に理解していません。入力テキストは次のようになります。txtファイルからのJava正規表現の入力

ピートは数学5 + 3とジェシカもsin(3)です。私は必要な出力で

ピート数学8とjesicaあまりにも0.14のように。

これを行う方法を知っている人からの正規表現と計算の助言が必要です。

マイコード:

final static Pattern PUNCTUATION = Pattern.compile("([\\s.,!?;:]){1,}"); 
final static Pattern LETTER = Pattern.compile("([а-яА-Яa-zA-Z&&[^sin]]){1,}"); 
    List<Sentence> sentences = new ArrayList<Sentence>(); 
    List<PartOfSentence> parts = new ArrayList<PartOfSentence>(); 
    StringTokenizer st = new StringTokenizer(text, " \t\n\r:;.!?,/\\|\"\'", 
      true); 

正規表現を使用してコード(動作しない):

while (st.hasMoreTokens()) { 

     String s = st.nextToken().trim(); 
     int size = s.length(); 
     for (int i=0; i<s.length();i++){ 
     //with regex. not working variant 
     Matcher m = LETTER.matcher(s); 
     if (m.matches()){ 
      parts.add(new Word(s.toCharArray())); 
     } 
     m = PUNCTUATION.matcher(s); 
     if (m.matches()){ 
      parts.add(new Punctuation(s.charAt(0))); 
     } 
     Sentence buf = new Sentence(parts); 
     if (buf.getWords().size() != 0) { 
      sentences.add(buf); 
      parts = new ArrayList<PartOfSentence>(); 
     } else 
      parts.add(new Punctuation(s.charAt(0))); 

正規表現なし(作業):

if (size < 1) 
      continue; 
     if (size == 1) { 
      switch (s.charAt(0)) { 
      case ' ':    
       continue; 
      case ',': 
      case ';': 
      case ':': 
      case '\'': 
      case '\"': 
       parts.add(new Punctuation(s.charAt(0))); 
       break; 
      case '.': 
      case '?': 
      case '!': 
       parts.add(new Punctuation(s.charAt(0))); 
       Sentence buf = new Sentence(parts); 
       if (buf.getWords().size() != 0) { 
        sentences.add(buf); 
        parts = new ArrayList<PartOfSentence>(); 
       } else 
        parts.add(new Punctuation(s.charAt(0))); 
       break; 
      default: 
       parts.add(new Word(s.toCharArray())); 
      } 

     } else { 
      parts.add(new Word(s.toCharArray())); 
     } 
    } 

答えて

0

これは、一致する数字もかなり複雑になる可能性があるため、解決するのは些細な問題ではありません。

まず、小数点と指数のフォーマットを考慮に入れて、正規表現"(\\d*(\\.\\d*)?\\d(e\\d+)?)"で数値を照合することができます。

第2に、解決したい表現には、少なくともバイナリ、単項、関数の3種類があります。それぞれについて、solveメソッドで照合するパターンを作成します。

第3に、thisまたはthisのようなreduceメソッドを実装できる多数のライブラリがあります。

以下の実装では、ネストされた式、たとえばsin(5) + cos(3)や式のスペースなどは処理されません。

private static final String NUM = "(\\d*(\\.\\d*)?\\d(e\\d+)?)"; 

public String solve(String expr) { 
    expr = solve(expr, "(" + NUM + "(!|\\+\\+|--))"); //unary operators 
    expr = solve(expr, "(" + NUM + "([+-/*]" + NUM + ")+)"); // binary operators 
    expr = solve(expr, "((sin|cos|tan)\\(" + NUM + "\\))"); // functions 

    return expr; 
} 

private String solve(String expr, String pattern) { 
    Matcher m = Pattern.compile(pattern).matcher(expr); 

    // assume a reduce method :String -> String that solve expressions 
    while(m.find()){ 
     expr = m.replaceAll(reduce(m.group())); 
    } 
    return expr; 
} 

//evaluate expression using exp4j, format to 2 decimal places, 
//remove trailing 0s and dangling decimal point 
private String reduce(String expr){ 
    double res = new ExpressionBuilder(expr).build().evaluate(); 
    return String.format("%.2f",res).replaceAll("0*$", "").replaceAll("\\.$", ""); 
} 
+0

hey!答えてくれてありがとう。私は自分のプロジェクトでreduceメソッドをreallizeする方法を完全には理解していません。あなたは私に少しの例を教えてもらえますか? –

+0

文字列 '3 + 4'を与えて式を評価し、' 7'を返すライブラリが数多くあります。私はexp4jのライブラリの1つを使って答えを更新しました。 'reduce'では、個々の式を評価する方法を実装することができます –

+0

ありがとう!クールなライブラリ、このように、すべてが動作しています。 –

0

私はあなたが探していることから始めることができると思いますあなたの入力文字列の "機能"マッチングのために。その後、すべてが一致しない関数が返されます。例えば

、この短いコードは、私はあなたが求めているものを、願っています:

クラスのMainメソッドで。

ピートがあまりに6 + 3数学3 + 3とジェシカのお気に入り:本当の仕事

import java.util.ArrayList; 
import java.util.StringTokenizer; 
import java.util.regex.Pattern; 

class AdditionPatternFuntion{ 
    public static boolean render(String s, ArrayList<String> renderedStrings){ 
     Pattern pattern = Pattern.compile("(\\d\\+\\d)"); 
     boolean match = pattern.matcher(s).matches(); 
     if(match){ 
      StringTokenizer additionTokenier = new StringTokenizer(s, "+", false); 
      Integer firstOperand = new Integer(additionTokenier.nextToken()); 
      Integer secondOperand = new Integer(additionTokenier.nextToken()); 
      renderedStrings.add(new Integer(firstOperand + secondOperand).toString()); 
     } 
     return match; 
    } 
} 

私は、この入力を使用して実行し

public class App { 
    StringTokenizer st = new StringTokenizer("Pete likes Mathematics 3+3 and Jessica too 6+3.", " \t\n\r:;.!?,/\\|\"\'", true); 

    public static void main(String[] args) { 
     new App(); 
    } 
    public App(){ 
     ArrayList<String> renderedStrings = new ArrayList<String>(); 
     while(st.hasMoreTokens()){ 
      String s = st.nextToken(); 
      if(!AdditionPatternFuntion.render(s, renderedStrings)){ 
       renderedStrings.add(s); 
      } 
     } 
     for(String s : renderedStrings){ 
      System.out.print(s); 
     } 
    } 
} 

クラス "AdditionPattern"。

私はgetthis出力:

ピートは数学6とジェシカあまりに9

あなたが同じことを行うことができます "罪()" 関数を処理するのが好き:新しいクラスを作成し、例えば ​​"SinPatternFunction"と同じことをします。

私は、抽象メソッド "render"を持つ抽象クラス "FunctionPattern"を作成し、AssitionPatternFunctionクラスとSinPatternFunctionクラスで実装する必要があると思います。 最後に、クラスを作成し、それを "PatternFunctionHandler"と呼んで、PatternFunction(SinPatternFunction、AdditionPatternFunctionなど)のリストを作成し、それぞれをレンダリングして結果を返すようにしましょう。

0

ご指定の要件は、正規表現を使用することです:

  1. コンポーネントに分割テキスト

を評価した内側の算術式と(言葉、...)

  • 戻りテキスト正規表現を使用して最初のステップを開始しましたが、まだ完了していません。完了後には、次のようになります。

    1. 算術(サブ)式を構成するコンポーネントを認識し、解析します。
    2. 認識された(下位の)式のコンポーネントを評価し、値を生成します。インミックス表記法で(サブ)式を評価するには、exists a very helpful answerがあります。
    3. 値の置換を元の文字列に戻す - 単純にする必要があります。

    部分式の後で明白な評価ができるように厳密に定義されたコンポーネントにテキスト分割する場合、私はnamed capturing groups in Javaを試してサンプルをコーディングしました。このサンプルは整数だけを扱いますが、浮動小数点は簡単に追加することができます。次のようにいくつかのテスト入力に

    サンプル出力した:グループの使用を取り込むという名前で

    Matching 'Pete like mathematic 5+3 and jesica too sin(3).' 
    WORD('Pete'),WS(' '),WORD('like'),WS(' '),WORD('mathematic'),WS(' '),NUM('5'),OP('+'),NUM('3'),WS(' '),WORD('and'),WS(' '),WORD('jesica'),WS(' '),WORD('too'),WS(' '),FUNC('sin'),FOPENP('('),NUM('3'),CLOSEP(')'),DOT('.') 
    Matching 'How about solving sin(3 + cos(x)).' 
    WORD('How'),WS(' '),WORD('about'),WS(' '),WORD('solving'),WS(' '),FUNC('sin'),FOPENP('('),NUM('3'),WS(' '),OP('+'),WS(' '),FUNC('cos'),FOPENP('('),WORD('x'),CLOSEP(')'),CLOSEP(')'),DOT('.') 
    Matching 'Or arcsin(4.2) we do not know about?' 
    WORD('Or'),WS(' '),WORD('arcsin'),OPENP('('),NUM('4'),DOT('.'),NUM('2'),CLOSEP(')'),WS(' '),WORD('we'),WS(' '),WORD('do'),WS(' '),WORD('not'),WS(' '),WORD('know'),WS(' '),WORD('about'),PUNCT('?') 
    Matching ''sin sin sin' the catholic priest has said...' 
    PUNCT('''),WORD('sin'),WS(' '),WORD('sin'),WS(' '),WORD('sin'),PUNCT('''),WS(' '),WORD('the'),WS(' '),WORD('catholic'),WS(' '),WORD('priest'),WS(' '),WORD('has'),WS(' '),WORD('said'),DOT('.'),DOT('.'),DOT('.') 
    

    を、私はそれがコンパイルPatternまたは取得Matcher APIが存在グループ名へのアクセスを提供しないことを不便ました。以下のサンプルコード。

    import java.util.*; 
    import java.util.regex.*; 
    
    import static java.util.stream.Collectors.joining; 
    
    public class Lexer { 
        // differentiating _function call opening parentheses_ from expressions one 
        static final String S_FOPENP = "(?<fopenp>\\()"; 
        static final String S_FUNC = "(?<func>(sin|cos|tan))" + S_FOPENP; 
        // expression or text opening parentheses 
        static final String S_OPENP = "(?<openp>\\()"; 
        // expression or text closing parentheses 
        static final String S_CLOSEP = "(?<closep>\\))"; 
        // separate dot, should help with introducing floating-point support 
        static final String S_DOT = "(?<dot>\\.)"; 
        // other recognized punctuation 
        static final String S_PUNCT = "(?<punct>[,!?;:'\"])"; 
        // whitespace 
        static final String S_WS = "(?<ws>\\s+)"; 
        // integer number pattern 
        static final String S_NUM = "(?<num>\\d+)"; 
        // treat '*/+ -' as mathematical operators. Can be in dashed text. 
        static final String S_OP = "(?<op>\\*|/|\\+|-)"; 
        // word -- refrain from using \w character class that also includes digits 
        static final String S_WORD = "(?<word>[a-zA-Z]+)"; 
    
        // put the predefined components together into single regular expression 
        private static final String S_ALL = "(" + 
         S_OPENP + "|" + S_CLOSEP + "|" + S_FUNC + "|" + S_DOT + "|" + 
         S_PUNCT + "|" + S_WS + "|" + S_NUM + "|" + S_OP + "|" + S_WORD + 
        ")"; 
        static final Pattern ALL = Pattern.compile(S_ALL); // ... & form Pattern 
    
        // named capturing groups defined in regular expressions 
        static final List<String> GROUPS = Arrays.asList(
         "func", "fopenp", 
         "openp", "closep", 
         "dot", "punct", "ws", 
         "num", "op", 
         "word" 
        ); 
        // divide match into components according to capturing groups 
        static final List<String> tokenize(Matcher m) { 
         List<String> tokens = new LinkedList<>(); 
         while (m.find()){ 
          for (String group : GROUPS) { 
           String grResult = m.group(group); 
           if (grResult != null) 
            tokens.add(group.toUpperCase() + "('" + grResult + "')"); 
          } 
         } 
    
         return tokens; 
        } 
    
        // some sample inputs to test 
        static final List<String> INPUTS = Arrays.asList(
         "Pete like mathematic 5+3 and jesica too sin(3).", 
         "How about solving sin(3 + cos(x)).", 
         "Or arcsin(4.2) we do not know about?", 
         "'sin sin sin' the catholic priest has said..." 
        ); 
    
        // test 
        public static void main(String[] args) { 
         for (String input: INPUTS) { 
          Matcher m = ALL.matcher(input); 
          System.out.println("Matching '" + input + "'"); 
          System.out.println(tokenize(m).stream().collect(joining(","))); 
         } 
        } 
    }