Java-将文件扫描器与用户输入扫描器相结合

Java-将文件扫描器与用户输入扫描器相结合,java,java-8,refactoring,java.util.scanner,read-eval-print-loop,Java,Java 8,Refactoring,Java.util.scanner,Read Eval Print Loop,Edit2:下面是一些示例代码,这是因为人们确实以原始形式回答了这个问题 以下是我和我的CS教授的一个总结: 我们有一个作业,要求学生为修改后的SQL子集编写一个基本命令接口。这并不重要,但提供了上下文 需求说明命令可以在文件中,也可以在命令提示下输入。很明显,扫描器对此似乎很自然 因此,对于这个问题,我将参考我正在进行的学生项目的这一承诺: 两个相关的类是和 问题出现在AppManager中的ln 51转发和ExecuteCommand中的ln 56转发的组合上。扫描仪用于管理用户输入的循

Edit2:下面是一些示例代码,这是因为人们确实以原始形式回答了这个问题


以下是我和我的CS教授的一个总结:

我们有一个作业,要求学生为修改后的SQL子集编写一个基本命令接口。这并不重要,但提供了上下文

需求说明命令可以在文件中,也可以在命令提示下输入。很明显,扫描器对此似乎很自然

因此,对于这个问题,我将参考我正在进行的学生项目的这一承诺:

两个相关的类是和

问题出现在AppManager中的ln 51转发和ExecuteCommand中的ln 56转发的组合上。扫描仪用于管理用户输入的循环和扫描仪用于管理逐行读取文件的循环不兼容;因此,我和我的教授都无法找到一种方法将两种扫描仪组合成一种方法

换句话说,这两种结构在一天结束时是极其相似的,并且可能是一个相同的结构,但我们无法发现这不比当前的情况更糟糕

是否有一种方法可以编写扫描仪,使其既可用于用户输入,也可用于用户输入的文件输入?

一些快速观察:

  • 我在我的代码中注意到事情开始有点臭;也就是说,事情直觉上是错的。这发生在ExecuteCommand中,因为它是两个命令中要编写的第二个

  • 这段代码松散地遵循。我的母语是Pythic和/或C++。一些习语和处理事情的方式无疑会反映这一点

  • 我的教授很清楚,至少我打算发布这个问题,他和我一样好奇和激动。当他解决了这个项目,以确保它是可行的,需要多长时间,他碰到了同样的绊脚石,找不到一个他满意的解决方案

  • 编辑:语境很重要;请在指定的位置打开这两个文件并检查一下。现在最终发生的事情是,这两种扫描器几乎相同,但由于Java中两种类型的I/O的工作方式,其中一种只适用于文件,另一种只适用于用户输入。从表面上看,我突然意识到这个问题听起来可能比实际情况要密集得多。(是的,任何使用扫描器的方法都可以解析字符串,而不管其源是什么,但这里的情况更多的是关于在两个不同的源上使用相同的扫描器,这主要是因为它是如何使用的)


Edit2:在一些评论之后,这里是一些形式的代码,演示了核心问题

public void doFile()
{
    // set scanner up against some URI; this is messy but it's a
    // "point of the matter" thing
    Scanner cin = new Scanner(aFile);

    // read over file
    while (cin.hasNextLine())
    {
        // this is actually a lot more complicated, but ultimately we're
        // just doing whatever the next line says
        doWhatItSays(cin.nextLine());
    }
}

public void doREPL()
{
    // set scanner up against user input - this is the actual line
    Scanner cin = new Scanner(System.in);


    Boolean continueRunning = true;

    while(continueRunning)
    {
        // pretty print prompt
        System.out.println("");
        System.out.print("$> ");

        // This, like before, is a lot more complicated, but ultimately
        // we just do whatever it says. (One of the things it may say
        // to do is to set continueRunning to false.)
        doWhatItSays(cin.nextLine());
    }
}
他们都只是简单地扫描一个输入,然后按它说的做;如何将这两种方法合并为一种?(是的,它既快又乱;它至少把重点和基本的评论都讲清楚了。)

我想。这基本上是从罗伯特·塞吉威克和凯文·韦恩的《普林斯顿算法》第四版中摘取的
StdIn
类。我在他们的Coursera MOOC算法第一部分中使用了这个类来读取文件或cmd中的输入

编辑
合并上面提到的类

编辑2
在类中的
中,您有两种方法:

/**
     * Initializes an input stream from a file.
     *
     * @param  file the file
     * @throws IllegalArgumentException if cannot open {@code file}
     * @throws IllegalArgumentException if {@code file} is {@code null}
     */
    public In(File file) {
        if (file == null) throw new IllegalArgumentException("file argument is null");
        try {
            // for consistency with StdIn, wrap with BufferedInputStream instead of use
            // file as argument to Scanner
            FileInputStream fis = new FileInputStream(file);
            scanner = new Scanner(new BufferedInputStream(fis), CHARSET_NAME);
            scanner.useLocale(LOCALE);
        }
        catch (IOException ioe) {
            throw new IllegalArgumentException("Could not open " + file, ioe);
        }
    }

/**
*初始化来自给定{@link Scanner}源的输入流;配合使用
*{@code new Scanner(String)}读取字符串。
*
*请注意,这不会创建防御副本,因此
*当您继续阅读时,扫描仪将发生变异。
*
*@param scanner打开扫描仪
*如果{@code scanner}为{@code null},则@throws IllegalArgumentException
*/
公共输入(扫描仪){
如果(scanner==null)抛出新的IllegalArgumentException(“scanner参数为null”);
this.scanner=扫描器;
}
您可以根据用户输入调用适当的方法(比如检查第一个参数是否是文件,这很容易做到),然后还有大量其他方法,如
hasNextLine
readChar
。这是一节课的全部内容。不管使用哪个构造函数,都可以调用相同的方法。

我认为。这基本上是从罗伯特·塞吉威克和凯文·韦恩的《普林斯顿算法》第四版中摘取的
StdIn
类。我在他们的Coursera MOOC算法第一部分中使用了这个类来读取文件或cmd中的输入

编辑
合并上面提到的类

编辑2
类中的
中,您有两种方法:

/**
     * Initializes an input stream from a file.
     *
     * @param  file the file
     * @throws IllegalArgumentException if cannot open {@code file}
     * @throws IllegalArgumentException if {@code file} is {@code null}
     */
    public In(File file) {
        if (file == null) throw new IllegalArgumentException("file argument is null");
        try {
            // for consistency with StdIn, wrap with BufferedInputStream instead of use
            // file as argument to Scanner
            FileInputStream fis = new FileInputStream(file);
            scanner = new Scanner(new BufferedInputStream(fis), CHARSET_NAME);
            scanner.useLocale(LOCALE);
        }
        catch (IOException ioe) {
            throw new IllegalArgumentException("Could not open " + file, ioe);
        }
    }

/**
*初始化来自给定{@link Scanner}源的输入流;配合使用
*{@code new Scanner(String)}读取字符串。
*
*请注意,这不会创建防御副本,因此
*当您继续阅读时,扫描仪将发生变异。
*
*@param scanner打开扫描仪
*如果{@code scanner}为{@code null},则@throws IllegalArgumentException
*/
公共输入(扫描仪){
如果(scanner==null)抛出新的IllegalArgumentException(“scanner参数为null”);
this.scanner=扫描器;
}

您可以根据用户输入调用适当的方法(比如检查第一个参数是否是文件,这很容易做到),然后还有大量其他方法,如
hasNextLine
readChar
。这是一节课的全部内容。无论使用哪种构造函数,都可以调用相同的方法。

我会将输入从主代码中分离出来,这样您就有了一个接口
源代码
,您可以为命令轮询该接口,然后有两个具体实现

  • FileSource
    仅从文件中读取
  • 交互资源
    向用户显示提示的资源
    public void doFile() {
        try(Scanner cin = new Scanner(aFile)) {
            // read over file
            while (cin.hasNextLine()) {
                commonLoopBody(cin);
            }
        }
    }
    
    public void doREPL() {
        // set scanner up against user input - this is the actual line
        Scanner cin = new Scanner(System.in);
        boolean continueRunning = true;
    
        while(continueRunning) {
            // pretty print prompt
            System.out.printf("%n$> ");
            commonLoopBody(cin);
        }
    }
    
    private void commonLoopBody(Scanner cin) {
        // this is actually a lot more complicated, but ultimately we're
        // just doing whatever the next line says
        doWhatItSays(cin.nextLine());
    }
    
    public void doFile() {
        try(Scanner cin = new Scanner(aFile)) {
            commonLoop(cin, cin::hasNextLine, ()->{});
        }
    }
    
    public void doREPL() {
        boolean continueRunning = true;
        commonLoop(new Scanner(System.in),()->continueRunning,()->System.out.printf("%n$> "));
    }
    
    private void commonLoop(Scanner cin, BooleanSupplier runCondition, Runnable beforeCommand){
        while(runCondition.getAsBoolean()) {
            beforeCommand.run();
            // this is actually a lot more complicated, but ultimately we're
            // just doing whatever the next line says
            doWhatItSays(cin.nextLine());
        }
    }
    
    static String EXAMPLE_INPUT =
       "a single line command;\n"
     + "-- a standalone comment\n"
     + "a multi\n"
     + "line\n"
     + "-- embedded comment\n"
     + "command;\n"
     + "multi -- line\n"
     + "command with double minus;\n"
     + "and just a last command;";
    
    public static void main(String[] args) {
        Scanner s = new Scanner(EXAMPLE_INPUT).useDelimiter(";(\\R|\\Z)");
        while(s.hasNext()) {
            String command = s.next().replaceAll("(?m:^--.*)?+(\\R|\\Z)", " ");
            System.out.println("Command: "+command);
        }
    }