Java 可以在没有明确分隔符的情况下“分隔输入本身”吗?

Can Java "delimit input itself", without explicit delimiters?

提问人:Sergey Zolotarev 提问时间:12/19/2022 最后编辑:Sergey Zolotarev 更新时间:3/17/2023 访问量:95

问:

我的计算器,我需要为我的测试任务申请 Java 课程,运行良好。但是我想解决一个问题。例如,如果您输入“5+3”而不是“5 + 3”,则不起作用。我的计算器是否足够智能,可以在没有明确分隔符(如空格)的情况下分隔输入?

换句话说,例如,如何使扫描仪的输入拆分为五个标记:、、、和?如果我不必彻底修改我的整个代码,那就更好了!5+32 *25+32*2

import java.util.Scanner;
import java.io.IOException;
import java.text.DecimalFormat;

public class Introduction {
  private static double firstNum;
  private static double secondNum;
  private static double result;
  private static boolean dontKnow;
  private static String firstNumS;
  private static String secondNumS;
  private static String operationS;
  private static final DecimalFormat df = new DecimalFormat("#.##");

  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    firstNumS = scanner.next();
    operationS = scanner.next();
    secondNumS = scanner.next();

    if(firstNumS.compareTo(operationS) > 15){
      switch(firstNumS){
      case "I":
      firstNum = 1;
      break;
      case "II":
      firstNum = 2;
      break;
      case "III":
      firstNum = 3;
      break;
      case "IV":
      firstNum = 4;
      break;
      case "V":
      firstNum = 5;
      break;
      case "VI":
      firstNum = 6;
      break;
      case "VII":
      firstNum = 7;
      break;
      case "VIII":
      firstNum = 8;
      break;
      case "IX":
      firstNum = 9;
      break;
      case "X":
      firstNum = 10;
      break;
      default:
      System.out.println("I don't know the first number!");
      dontKnow = true; }}
    else {
    firstNum = Integer.decode(firstNumS);
      if(firstNum > 10){
      System.out.println("It appears, the first number is too big for me!");
      dontKnow = true;
      }
      }

    if(secondNumS.compareTo(operationS) > 15) {
      switch(secondNumS){
      case "I":
      secondNum = 1;
      break;
      case "II":
      secondNum = 2;
      break;
      case "III":
      secondNum = 3;
      break;
      case "IV":
      secondNum = 4;
      break;
      case "V":
      secondNum = 5;
      break;
      case "VI":
      secondNum = 6;
      break;
      case "VII":
      secondNum = 7;
      break;
      case "VIII":
      secondNum = 8;
      break;
      case "IX":
      secondNum = 9;
      break;
      case "X":
      secondNum = 10;
      break;
      default:
      System.out.println("I don't know the second number!");
      dontKnow = true; }}
    else {
    secondNum = Integer.decode(secondNumS);
      if(secondNum > 10) {
      System.out.println("It appears, the second number is too big for me!");
      dontKnow = true; }}
  
    if(operationS.equals("+")) {
      result = firstNum + secondNum; }
    else if(operationS.equals("-")) {
      result = firstNum - secondNum; }
    else if(operationS.equals("*")){
      result = firstNum * secondNum; }
    else if(operationS.equals("/")){
      result = firstNum / secondNum; }
    else {
      System.out.println("I don't know such an operation!");
      dontKnow = true; }

    if(!(operationS.equals("/") && secondNum == 0)) {
      if(!dontKnow) {
        if(result / (int)result != 1) {
          if(String.valueOf(result).equals(df.format(result))) {
          System.out.println("It's " + result + "!"); }
          else {
          System.out.println("It's approximately " + df.format(result) + "!"); }}
        else {
        System.out.println("It's " + (int)result + "!"); }}}
    else {
      if(!dontKnow) {
      System.out.println("Gosh! I tried to divide it by zero, as you requested, but my virtual head nearly exploded! I need to recover..."); }
      else {
      System.out.println("Besides, you can't even divide by zero, I'm so told!"); }}
  }
}
Java java.util.scanner 分隔符

评论

0赞 access violation 12/19/2022
就我个人而言,我根本不会使用扫描仪。我读了一行包含表达式的行,然后解析了表达式。这些算法是众所周知的,但也许这不是初学者级别的。

答:

2赞 rzwitserloot 12/19/2022 #1

假设您使用的是扫描仪,是的,它可以。扫描程序的操作基于正则表达式用作分隔符的概念:正则表达式的每个匹配都会进行分隔,并且正则表达式匹配的任何内容都会被扔掉(因为没有人“关心”读取空格或逗号或其他什么)。然后,扫描程序会在分隔符之间为您提供内容。

因此,要最终得到扫描仪流“5”、“+”和“3”,您需要一个分隔符来分隔“5”/“+”和“+”/“3”之间的空间,同时匹配 0 个字符,否则这些字符将被抛出。

您可以使用正则表达式 lookahead/lookbehind 来做到这一点。您希望在左边有一个数字,在右边有一个运算符,反之亦然:

String test = "53 + 2*35- 8";
Scanner s = new Scanner(test);
s.useDelimiter("\\s+|(?:(?<=\\d)(?=[-+/*]))|(?:(?=\\d)(?<=[-+/*]))");
while (s.hasNext()) {
  System.out.println("NEXT: '" + s.next() + "'");
}

要打破这个复杂的正则表达式,请执行以下操作:

  • A|B|C表示:A 或 B 或 C。这是这个正则表达式的“最外层”部分,我们正在寻找 3 个不同的事物之一来拆分。
  • \\s+表示:1 个或多个空格字符。因此,输入将拆分为 和 。空格被消耗掉(即被扔掉,而不是你的代币的一部分)。"5 20"520
  • 或者,正向后看(意思是:如果向后看,你会看到 X),则匹配),X 在这里 - 一个数字。然后,我们还有一个积极的展望:意味着:检查 X 是否在这里,但不要消耗它(否则它会被抛出,请记住,正则表达式描述分隔符,并且分隔符被抛出)。我们展望其中一个符号。(?<=X)\\d(?=X)
  • 或者,那个,但翻转了(首先是运算符,然后是数字)。

注意:如果你想避免正则表达式的复杂性,你可以遍历每个字符,但你要构建一个小的状态机,并且必须处理连续的、非空格分隔的数字:你需要把它们组合起来(不是、、、、、——它是)。10 + 2010+2010+20

NB2:如果你也想支持,你可以适当地编辑正则表达式(它们本质上是“运算符”,并进入运算符列表),但是,在某些时候,你基本上是在描述一种形式语言的语法,应该开始研究解析器生成器。但这比任何一个都要复杂得多。()

评论

0赞 rzwitserloot 12/24/2022
它不完全相同;唯一的区别在于 VS . 只是把东西组合在一起,就像.有十亿个关于正则表达式的教程,包括在线工具,可以让你写一些文本,写一个正则表达式,用花哨的颜色向你显示哪些部分与什么匹配。(?=)(?<)(?:X)(5 + 2)
0赞 Sergey Zolotarev 1/14/2023
\\s 和 \s 的意思一样吗?🤔
0赞 rzwitserloot 1/15/2023
是的。Java 本身使用反斜杠作为转义机制,因此直接编写不会编译:与正则表达式不同(程序员奇怪的选择,例如转义不需要转义的东西,只是猜测),java 在它认为它或你感到困惑时会出错(这样做是正确的,更快的错误修复很重要)。 因此生成一个包含 2 个字符的字符串。将其发送到您的正则表达式解析器,您最终会得到一个表示“空格字符”的正则表达式。"\s""\\s"