Возможно ли чтобы после введения в консоли n-ного символа массива программа автоматически запускалась (без Enter).

396
18 апреля 2017, 08:01

Только начал изучать Java. Дали простенькую задачку, но чтобы при этом программа реагировала на ошибки пользователя.Задача вот:Составить линейную программу, печатающую значение true, если указанное высказывание является истинным, и false — в противном случае: Сумма двух первых цифр заданного четырехзначного числа равна сумме двух его последних цифр. Я сделал так,что изначально число введённое с клавиатуры типа String.Потом переводиться в массив Char. И вот собственно мой вопрос. Возможно ли чтобы после введения в консоли 4 символа массива программа автоматически запускалась (без Enter).

Answer 1

Поскольку не совсем ясно, что именно вы имеете ввиду под запуском программы я отвечу на две возможных интерпретации вопроса.

Управление терминалом до запуска

Если вы предполагаете, что операционная система запустит JVM в момент когда вы напишете последний символ следующей команды:

$ java com.example.Main 4532

то к сожалению это сделать невероятно сложно и сильно зависит от предоставляемых настроек терминала операционной системы и из вашей программы вы не сможете это сделать, поскольку управление вашей программе не передастся до тех пор пока она не будет запущена, а изменить вы хотите именно поведение запуска.

Управление терминалом внутри программы

Теперь рассмотрим другой вариант. Допустим вы запускаете свое приложение аргументов, после чего пользователь видит предложение ввести четырехзначное число:

$ java com.example.Main
Enter 4-digit number: 

И в этом случае мы рассматриваем задачу запустить обработку входных данных как только пользователь введет 4 цифры.

Короткий ответ звучит как "Да, это возможно". В принципе в программировании любое поведение возможно реализовать. Однако ответ будет не кроссплатформенный, пока вы не позаботитесь об этом, или же не используете библиотеку, которая уже об этом заботится. Например jCurses предоставит необходимый функционал.

Почему я вообще говорю о том, что надо предусматривать конкретную платформу? Дело в том, что System.in соединяется с системным терминалом, который в большинстве случаев по умолчанию реализует буферизацию ввода, то есть передает программе входные данные после ввода enter от пользователя.

Если здоровое любопытство программиста ведет вас к тому, чтобы таки реализовать желаемое вами поведение, давайте напишем наивный (и, стало быть, самый короткий) рабочий вариант, с которого можно начать удовлетворять любопытство.

Сходу я могу написать вместе с вами программу, которая будет реализовывать необходимое вам поведение на unix-подобных системах. Заранее приношу извинения за игнорирование каноничных правил обработки ошибок систем ввода/вывода: я уверен, что вы в состоянии самостоятельно дополнить программу до безопасного варианта.

Начнем с реализации метода считывания n цифр из потока:

private static char[] readInRawMode(int numberOfCharacters, InputStream stream) throws IOException {
    char [] result = new char[numberOfCharacters];
    InputStreamReader is = new InputStreamReader(stream);
    int characterCount = 0;
    while (characterCount < numberOfCharacters) {
        char aChar = (char) is.read();
        if (aChar < '0' || aChar > '9') { //проверим, что это цифра
            continue;
        }
        result[characterCount] = aChar;
        characterCount++;
    }
    return result;
}

Если мы сейчас используем этот метод в программе, например readInRawMode(4, System.in), то вызов is.read() заблокируется до тех пор, пока пользователь не нажмет enter. Для того чтобы избежать этого поведения, нам нужно перевести текущий системый терминал в режим raw перед вызовом метода и вернуть его в режим cooked после завершения метода обработки. В режиме raw терминал не повторяет символы вводимые пользователем на экране (как при введении пароля в терминале) и мы должны сделать это самостоятельно. Более того, в raw терминал перестанет реагировать на спец символы включая символ перевода каретки и enter от пользователя также будет игнорироваться и не отображаться до тех пор пока он не введет необходимое количество цифр. Это как раз то поведение которое нас устраивает.

Собирая все описанное вместе мы получим что то вроде подобного:

package com.alex;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.print("Enter 4-digit number : ");
        char [] input = interactiveRead();
        System.out.println();
        System.out.println("User input: " + Arrays.toString(input));
    }
    private static char[] interactiveRead() throws IOException, InterruptedException {
        String[] raw = {"/bin/sh", "-c", "stty raw </dev/tty"}; //команда перевода текущего терминала в режим raw
        String[] cooked = {"/bin/sh", "-c", "stty cooked </dev/tty"}; //команда перевода текущего терминала в режим cooked
        Process p = Runtime.getRuntime().exec(raw);
        p.waitFor();//ожидаем перевода терминала
        try {
            return readInRawMode(4, System.in);
        } finally {
            p = Runtime.getRuntime().exec(cooked); //возвращаем терминал к изначальному виду вне зависимости от ошибок
            p.waitFor();
        }
    }
    private static char[] readInRawMode(int numberOfCharacters, InputStream stream) throws IOException {
        char [] result = new char[numberOfCharacters];
        InputStreamReader is = new InputStreamReader(stream);
        int characterCount = 0;
        while (characterCount < numberOfCharacters) {
            char aChar = (char) is.read();
            if (aChar < '0' || aChar > '9') {
                continue;
            }
            System.out.print(aChar); // повторяем "правильный" символ на экране, чтобы пользователь понял что он все делает правильно
            result[characterCount] = aChar;
            characterCount++;
        }
        return result;
    }
}

Обсуждение и замечания

В действительноси, в большинстве случаев нет смысла писать сложную логику "богатого" взаимодействия пользователя с терминалом. Это дает сильное усложнение логики приложения (то, что мы описали только что недостаточно: необходимо позаботиться об ошибках выполнения и управляющих комманд таких как ^C в терминале), а профит минимальный: в современном мире пользователи предпочитают графические интерфейсы, а люди привыкшие работать с терминалом и так поймут что происходит и что именно нужно вводить: это люди технического склада ума.

Также хочу заметить, что написанное приложение заточено на запуск из терминала руками и только тогда оно будет корректно работать. В случае запуска через IDE ее родной терминал не переведется в необходимый режим, поскольку используется не стандартный системный терминал, а его обертка, которая также реализует буферизацию. Надеюсь это делает еще более понятной сложно написания универсального решения для вашей задачи.

READ ALSO
proguard для mainActivity

proguard для mainActivity

ЗдравствуйтеИспользую proguard для своего приложения в android studio

207
Написание своего класса Lock

Написание своего класса Lock

У меня задача написать свой механизм блокировок LockИ я нашел уже готовый самописный пример, упрощенной версии этого механизма:

246