繁体   English   中英

验证语法-Java-语法-正则表达式

[英]Verifying syntax - Java - Grammar - Regular expressions

在开始解释之前,请允许我声明这不是“请做我的家庭作业”问题。 我将发布我的代码和假设或猜测,但是由于无法解决此问题,我正在寻求您的帮助,不仅发现此问题很有趣,而且还是学习非常有用的技能的好机会。 该练习是我大学一项家庭作业的一部分,并且截止日期已经结束。 那我为什么要寻求帮助? 好吧,老师只会提供成绩分数,而不会提供有关如何解决练习的实际解释(这是大学的政策),直到下一年,即明年。 我等不了那么久,我对如何解决这个问题很感兴趣。 这是演说摘要:“在某些孤立的岛上,他们拥有自己的语言,但是大多数人会犯语法错误,因此他们的州长决定要求开发人员发布某些软件,并遵守他们的语言规则,更正语法”。 这些是规则:

  1. 该语言中唯一的字符是从'g'到'p'加上'A','F','D','Q','X'的字符。
  2. 从“ g”到“ p”的每个字符都是有效的句子。
  3. 如果“ s”是有效的句子,那么“ As”也有效。
  4. 如果“ s”和“ t”是有效语句,则“ Fst”,“ Dst”,“ Qst”和“ Xst”也是有效语句。
  5. 从1到4的规则是确定正确语法的唯一有效规则。

这对我们许多人来说还不够清楚,因此我们向老师询问了第3点和第4点,他解释说-因为's'实际上不是有效的句子,这只是一个例子-他们想表示'A '只能跟1个小写字母,F,D,Q和X只能跟2个有效的小写字母。

好吧,对于许多了解正则表达式的人来说,这听起来并不那么复杂。 实际上,我们没有教过正则表达式,只有JUnit无关吗? 好吧,事实是该练习实际上是JUnit研讨会的一部分,是的,我和您一样惊讶。 那么,为什么我要提起常规表达主题呢? 好吧,老师评论说,一些学生建议他使用正则表达式可以轻松解决该练习。

我正在使用NetBeans在Java中进行开发,并且我将发布到目前为止的代码中最重要的部分:

Pattern patronUno = Pattern.compile("[ghijklmnop]");
Pattern patronDos = Pattern.compile("A*[ghijklmnop]|A*");
Pattern patronTres = Pattern.compile("([FDQX]+([ghijklmnop]{2,2}))");
Pattern patronCuatro = Pattern.compile("[A][FDQX]");

public String validar(String sentencia){        
    Matcher coincidenciaUno = patronUno.matcher(sentencia);
    Matcher coincidenciaDos = patronDos.matcher(sentencia);
    Matcher coincidenciaTres = patronTres.matcher(sentencia);
    Matcher coincidenciaCuatro = patronCuatro.matcher(sentencia);

    if(coincidenciaUno.matches() || coincidenciaDos.matches() || 
                coincidenciaTres.matches() || coincidenciaCuatro.matches())
        return "YES";
    else
        return "NO";
}

经老师预先批准,我的第一个测试用例是:

示例输入示例输出

  • Fg NO
  • 西耶是
  • AXij是
  • Dklm NO

现在,AXij是有效的,因为A对每个人都有效,并且Xij也有效,因此将Xij添加到A就像向其添加一个单字符有效语句。 而我的朋友们,是我无法处理的部分练习; 我无法学习-如果有办法-如何连接两个有效句子。 您可以帮我解决那部分需求吗?

通过发布的代码,我得到了以下结果:

示例输入示例输出

  • Fg NO
  • 西耶是
  • 轴号
  • Dklm NO
  • AAg是
  • AA是的
  • 是的
  • 放大器编号
  • 编号

例如,我无法将AXij视为有效语句。 但是我可以成功地验证Fx为无效,Xij为有效,Dklm为无效等。

在此先感谢您的帮助。 我发布此邮件的原因是,我一直在通过本网站搜索可以解释此问题的示例,但其中任何一个对我来说都很清楚。

您所描述的语法非常简单且完全明确,因此,如果您不想深入使用像antlr或JavaCC这样的功能强大的解析器生成器,则可以使用像这样的单个递归方法来完成这项工作:

public int consumeSentence(String str, int startOffset) {
  char c = str.charAt(startOffset);
  if(c >= 'g' && c <= 'p') {
    return 1;
  } else if(c == 'A') {
    // A<s>
    return 1 + consumeSentence(str, startOffset + 1);
  } else if(c == 'F' || c == 'D' || c == 'Q' || c == 'X') {
    // F<s><t>
    int s1 = consumeSentence(str, startOffset + 1); // s
    int s2 = consumeSentence(str, startOffset + 1 + s1); // t
    return 1 + s1 + s2;
  } else {
    throw new IllegalArgumentException("illegal sentence");
  }
}

这将尝试从给定位置开始的字符串中使用内容,直到到达有效句子的末尾为止,并返回所使用的字符数。 要检查字符串s是否为有效语句,请调用consumeSentence(s, 0)然后查看返回值是否等于原始字符串的长度。 如果返回的值更少,或者引发异常,则原始字符串不是有效的句子。

解析器生成器会为您构建这种逻辑,但是这种情况非常简单,您可以手动对其进行编码。

我没有足够的理论知识来证明这是上下文无关的语法。 尽管众所周知的正则表达式风格可以匹配上下文相关的语法,但没有递归(Perl / PCRE)或平衡组(.NET),但正则表达式无法解决上下文无关的简单问题,例如括号平衡。

顺便说一下,这是语法:

S -> 'g' | 'h' | ... | 'p'
S -> 'A'  S
S -> ( 'F' | 'D' | 'Q' | 'X' )  S  S

通常,在这种情况下,最好编写一个解析器。 但是,对于玩具问题,可以使用正则表达式从最低级别开始进行替换,并将叶子A[gp][FDQX][gp]{2}的结构减小为g (表示有效的句子)。

public static boolean checkGrammar(String input) {
    String prev = input;
    while (!(input = input.replaceAll("A[g-p]|[FDQX][g-p]{2}", "g")).equals(prev)) {
        prev = input;
    }

    return input.matches("[g-p]");
}

ideone上的演示

不确定我是否了解所有内容,但这似乎与您的示例相符:

^[A]?((A[g-p])|([FDQX][g-p]{2}))$

演示

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM