Запустите программу C с интерактивным вводом-выводом, используя ProcessBuilder - PullRequest
0 голосов
/ 18 мая 2018

Я разрабатываю программное обеспечение IDE для C / C ++ с использованием java (хотя есть много доступного, но я хочу своего), которое может компилировать и выполнять программы на C или C ++.Поэтому я попробовал простую программу для компиляции и выполнения программы на C в Java, используя Process и ProcessBuilder.

Вот моя простая программа на Java, которая компилирует и выполняет программу на C:

public class RunProgram {

    public static void main(String[] args) throws Exception {
        new ProcessBuilder("gcc", "-o", "first", "first.c").start().waitFor(); //To Compile the source file using gcc and wait for compilation


/*
    Although I've to handle error-stream but 
for now, my assumption is that there is no error 
in program.
*/


        ProcessBuilder run = new ProcessBuilder("./first");

        execute.redirectErrorStream(true);

        Process runProcess = run.start();

        StreamReader sr = new StreamReader(runProcess.getInputStream());

        new Thread(sr).start(); //A new thread to handle output of program . 

    //rest of coding to provide input using OutputStream of 'runProcess' and to close the stream.

    }
}

class StreamReader implements Runnable {

    private InputStream reader;

   public StreamReader(InputStream inStream) {
      reader = inStream;

   }

   @Override
   public void run() {

      byte[] buf = new byte[1024];

        int size = 0;
        try {

            while ((size = reader.read(buf)) != -1) {

                System.out.println(new String(buf));
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
   }
}

А вот и моя first.c программа.

#include<stdio.h>

int main() {
    int a;
    int k;

    printf("input a: ");

    scanf("%d", &a);
    for(k = 0; k < a; k++)
        printf("k = %d\n", k);
    return 0;
}

Я хочу создать интерактивную консоль IO, как и большинство IDE или командных терминалов (Терминал в ОС ОС Linux и командная строка в ОС Windows).Для приведенного выше примера: во-первых, он должен напечатать “Input a: “, а затем дождаться ввода данных и затем остальную часть программы. Но это не сработает, как я думал, так как это не сработаетпечатать результат оператора printf, который появлялся до scanf, пока я не предоставлю ввод, возможно, используя OutputStream.

Я нашел свою проблему в Google и посетил много ссылок, но не нашел решения.Между тем, я нашел эту ссылку, которая предлагает добавлять fflush после каждого оператора printf или использовать методы setbuf или setvbuf (из некоторых других подссылок) для очистки буфера.Но новый человек (который собирается изучать C) может не знать о fflush или об этих функциях, и он / она никогда не будет использовать его, поскольку это не требуется в других IDE или даже на терминалах. Как я могу решить эту проблему и собрать встроенную консоль для моей IDE

Вот пример того, что я хочу:

animation of Interactive IO

Ответы [ 2 ]

0 голосов
/ 18 мая 2018

Из приведенных выше комментариев подумайте, что здесь имеет смысл добавить небольшое объяснение того, как работает буферизация для потоков ввода / вывода.

Что происходит за кулисами при вызове printf(3), и тому подобное:данные записываются в буфер, пока буфер не заполнится или не произойдет какой-либо триггер.Содержимое буфера затем копируется из буфера в фактическое устройство вывода / другой буфер вывода ... Триггер обычно сталкивается с концом строки (\n в Linux / Unix).Таким образом, грубая версия этой буферизации выглядит так:

struct buffered_file_t {

    char* buffer;
    size_t capacity;
    size_t current_char;
    FILE* file;

};

void flush_buffered(struct buffered_file_t* file) {

    assert(0 != file);
    assert(0 != file->buffer);

    fwrite(file->buffer, file->current_char, 1, file->file);
    file->current_char = 0;

}


void print(struct buffered_file_t* file, const char* str) {

    assert(0 != file);
    assert(0 != file->buffer);
    assert(0 != str);

    for(size_t i = 0;  0 != str[i]; ++i) {

        if(file->current_char >= file->capacity - 1) flush_buffered(file);
        file->buffer[file->current_char++] = str[i];
        if('\n' == str[i]) flush_buffered(file);

    }

}

Теперь, если вы вызовете print как

const size_t BUFSIZE = 100;

struct buffered_file_t stdout_buffered = {
    .buffer = calloc(1, BUFSIZE),
    .capacity = BUFSIZE,
    .current_char = 0,
    .file= stdout,
};

print(&stdout_buffered, "Naglfar\n");
print(&stdout_buffered, "Surthur");

Вы не увидите, что Surthur появится в stdout,Чтобы записать его из буфера в stdout, необходимо либо

  • явно flush_buffered вызвать
  • Отключить буферизацию путем уменьшения размера буфера (buffered_file.capacity = 1 впример выше)

В вашем случае вы не можете явно вызвать fflush(3) (это то, что вы указали в качестве требования).таким образом, остается только отключить буферизацию.

Как это сделать, зависит от ОС, ИМХО.Для Linux посмотрите на stdbuf(1) из пакета Coreutils, чтобы узнать, как включить буферизацию для определенных потоков сторонних процессов.

0 голосов
/ 18 мая 2018

В GNU / Linux для отключения буферизации для стандартных потоков ввода-вывода вы можете использовать stdbuf(1) примерно так:

 ....
 ProcessBuilder run = new ProcessBuilder("stdbuf", "-o0", "./first");
 ....

Добавить -e0 и -i0 опций, если вы хотите отключить буферизацию для stderr и stdin.

Конечно, было бы лучше, если бы вам не приходилось полагаться на внешние инструменты, но можно было бы отключить буферизациюв вашем собственном коде - самое простое - взглянуть на источник stdbuf, но я полагаю, что в конечном итоге вам придется использовать JNI, а затем, я думаю, я просто придерживаюсь stdbuf...

...