简体   繁体   English

使用 Cactoos 从 Stdin 读取行

[英]Read line from Stdin with Cactoos

I'm searching for an approach to read single line from Stdin using the Cactoos library.我正在寻找一种使用 Cactoos 库从 Stdin 读取单行的方法。 I can do something like this:我可以做这样的事情:

System.out.println(
  new TextOf(
    new ReaderOf(
      new Stdin()
    )
  ).asString()
);

but this code blocks and reads Stdin/System.in until it's closed - I use Ctrl+D to stop reading and get my text printed.但是这段代码会阻塞并读取 Stdin/System.in 直到它关闭 - 我使用 Ctrl+D 停止阅读并打印我的文本。 Is there any approach to get behavior similar to BufferedReader#readLine()?是否有任何方法可以获得类似于 BufferedReader#readLine() 的行为?

Also I'd like to print some prompt before reading the Stdin, something like:另外,我想在阅读 Stdin 之前打印一些提示,例如:

System.out.println(
  new TextOf(
    new PromptedReaderOf( // desired decorator if I get Cactoos ideas right
      output,             // Output to display prompt-string to user
      "Type your text and press Enter: ",  // the prompt-string
      new ReaderOf(
        new Stdin()
      )
    )
  ).asString()
);

Is it possible with Cactoos or should I write my own decorators around Stdin for such interactive console application? Cactoos 是否可行,或者我应该围绕 Stdin 为此类交互式控制台应用程序编写自己的装饰器吗?

but this code blocks and reads Stdin/System.in until it's closed - I use Ctrl+D to stop reading但是这段代码会阻塞并读取 Stdin/System.in 直到它关闭 - 我使用 Ctrl+D 停止阅读

This happens because Reader has to read everything, including 'newline' (\\n\\r) characters, until there's nothing to read (Ctrl+D, stream becomes finite ).发生这种情况是因为 Reader 必须读取所有内容,包括 'newline' (\\n\\r) 字符,直到没有可读取的内容(Ctrl+D,流变为有限)。

When you type newline character by pressing 'enter' key, you wait for reader to stop reading infinite stream.当您按“回车”键输入换行符时,您等待阅读器停止阅读无限流。 That behaviour is reproduced, for example, inside BufferedReader::read**Line** .例如,该行为会在BufferedReader::read**Line**重现。

The TextOf(Reader) uses Reader::read instead (actually it happens inside ReaderAsBytes::asBytes). TextOf(Reader) 使用Reader::read代替(实际上它发生在 ReaderAsBytes::asBytes 内)。

Also I'd like to print some prompt before reading the Stdin Is it possible with Cactoos or should I write my own decorators around Stdin for such interactive console application?另外,我想在阅读 Stdin 之前打印一些提示 Cactoos 是否可行,或者我应该为这种交互式控制台应用程序围绕 Stdin 编写自己的装饰器吗?

So, yes, you need to implement new decorators to deal with your problem.所以,是的,您需要实现新的装饰器来处理您的问题。

You may need to use TextOf(Input) and create an Input decorator that produces a finite stream when 'newline' character occurs.您可能需要使用 TextOf(Input) 并创建一个Input装饰器,该装饰器在出现“换行”字符时生成有限流。

public class ReaderReadLineInput implements Input {
    //we don't want to use 'new' in methods
    private final Scalar<InputStream> inputStreamScalar;

    private ReaderReadLineInput(BufferedReader bufferedReader) {
        this.inputStreamScalar = new ScalarOf<InputStream>(
            br -> new InputStreamOf(
                br.readLine() //produces finite InputStream
            ),
            bufferedReader
        );
    }
    
    public ReaderReadLineInput(Reader reader){
        this(new BufferedReader(reader));
    }

    @Override
    public InputStream stream() throws Exception {
        return inputStreamScalar.value();
    }
}

Then you may want to connect this with your actual use-case (getting input from console by typing) and not to lose reusability of previous code, so create another Input decorator然后,您可能希望将其与您的实际用例(通过键入从控制台获取输入)相关联,而不是失去以前代码的可重用性,因此创建另一个Input装饰器

public class ManualConsoleInput implements Input {
    //and you still don't like 'new' in methods
    private final Scalar<Input> iptScalar;

    public ManualConsoleInput(Text charsetName) {
        // do you like Cactoos primitives?
        // there's a some workaround
        this.iptScalar = new ScalarOf<Input>(
            charset -> {
                return new ReaderReadLineInput(
                    new InputStreamReader(
                        new Stdin().stream(), 
                        charset.asString() 
                    )
                )
            },
            charsetName
        );
    }

    @Override
    public InputStream stream() throws Exception {
        return this.iptScalar.value().stream();
    }
}

To implement printing prompt text to console before getting user input, you also may need to create another decorator.要在获取用户输入之前将提示文本打印到控制台,您可能还需要创建另一个装饰器。

public class InputPrintsToConsole implements Input {  
    private final Runnable printingRunnable;
    private final Input origin;

    public InputPrintsToConsole(Text textToConsole, Input origin) {
        this.printingRunnable = new ConsolePrinting(textToConsole);
        this.origin = origin;
    }

    @Override
    public InputStream stream() throws Exception {
        printingRunnable.run();
        return origin.stream();
    }
}

Also remember that some people can use System::setOut when using your code to pipe standart output to file, for example.还请记住,例如,有些人在使用您的代码将标准输出通过管道传输到文件时可以使用System::setOut So you can't just rely on System god-object to get console output stream, only use it to get reference to console output stream, when you sure:所以你不能仅仅依靠 System God-object 来获取控制台输出流,只能使用它来获取对控制台输出流的引用,当你确定:

public class ConsolePrinting extends RunnableEnvelope {
    public ConsolePrinting(Text textToPrint) {
        super(
            new OutputStreamPrinting(
                System.out, // and encapsulate somewhere 
                textToPrint
            )
        );
    }
}

// splitting responsibility of objects
// and using decorators
public class OutputStreamPrinting implements Runnable { 
    private final PrintStream printStream;
    private final Text text;

    public OutputStreamPrinting(PrintStream printStream, Text text) {
        this.printStream = printStream;
        this.text = text;
    }
    
    public OutputStreamPrinting(OutputStream outputStream, Text text) {
        this(new PrintStream(outputStream), text);
    }
    
    @Override
    public void run() {
        this.printStream.println(this.text);
    }
}

And the top-level code from your example may look like this:您示例中的顶级代码可能如下所示:

System.out.println(
    new TextOf(
        new InputPrintsToConsole(
            new TextOf("Type your text and press Enter:"),
            new ManualConsoleInput(new TextOf("utf-8"))
        )
    )
);

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

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