4

I have a qt-app which can be invoked with:

cat bla.bin  | myapp

Whats the easiest way to read the entire input (stdin) into a QByteArray on Win,Mac and Linux?

I tired several things, but none of them seems to work (on windows):

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    QByteArray content;

    //---Test 1: hangs forever, reads 0
    while(!std::cin.eof()) {
        char arr[1024];
        int s = std::cin.readsome(arr,sizeof(arr));
        content.append(arr,s);
    }

    //---Test 2: Runs into timeout
    QFile in;
    if(!in.open(stdin,QFile::ReadOnly|QFile::Unbuffered)) {
        qDebug() << in.errorString();
    }
    while (in.waitForReadyRead(1000)) {
        content+=in.readAll();
    }
    in.close();

    return app.exec();
}

Am I having a Event-Loop Problem or shouldn't it work without?

Timo
  • 1,724
  • 14
  • 36
  • `Am I having a Event-Loop Problem?` Probably. Because event loop started after `app.exec();`. Try to read until file is closed or move reading after `exec` is called – Lol4t0 Sep 07 '14 at 14:43
  • Don't use `QFile` that way. `QFile` will never report "ready read". The approach with `std::cin` should work everywhere, are you compiling with `CONFIG+=console` under Windows? – peppe Sep 07 '14 at 15:19
  • According to the documentation I don't need an event loop. Yes I have CONFIG+=console and CONFIG-=app_bundle in my .pro file. It's not working on linux as well. – Timo Sep 07 '14 at 15:35

1 Answers1

4

The primary problem of actually reading from stdin stems from using readsome. readsome is generally not used to read from files (including stdin). Readsome is generally used for binary data on asynchronous sources. Technically speaking eof doesn't get set with readsome. read is different in that regard as it will set eof accordingly. There is an SO question/answer here that may be of interest. If you are supporting Linux and Windows and reading stdin, you have to be aware that on Windows stdin isn't opened in binary mode (neither is stdout). On Windows you would have to use _setmode on stdin. One way to do this is with #ifdefs using Q_OS_WIN32. Using QFile doesn't resolve this issue.

In the code you are trying to create it doesn't appear you are interested in actually having an event loop. You can still use QT objects like QByteArray without an event loop. In your code you read data in from stdin (cin) and then you executed return app.exec(); which put your console application into a loop waiting for events. You didn't add any events to the QT Event queue prior to app.exec(); so effectively the only thing you can do is end your application with control-c. If no event loop is needed then code like this should suffice:

#include <QCoreApplication>
#include <iostream>

#ifdef Q_OS_WIN32
#include <fcntl.h>
#include <io.h>
#endif

int main()
{
    QByteArray content;

#ifdef Q_OS_WIN32
    _setmode(_fileno(stdin), _O_BINARY);
#endif

    while(!std::cin.eof()) {
        char arr[1024];
        std::cin.read(arr,sizeof(arr));
        int s = std::cin.gcount();
        content.append(arr,s);
    }
}

Notice how we used a QByteArray but didn't have a QCoreApplication app(argc, argv); and a call to app.exec();

Community
  • 1
  • 1
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • 1
    The use of a `QObject` task here is pointless, since it will block. – Kuba hasn't forgotten Monica Sep 08 '14 at 19:16
  • Which code I wrote above blocks? The first example is likely closer to what the original poster intended without an event loop. If he wanted to do an event loop the code I produced executes what he wanted as a task and then exits. It doesn't block. It isn't a very useful example but it still does the same thing for what he asked. The OP may not have been aware of how to get a Console Application to deal with an event and have it exit. – Michael Petch Sep 08 '14 at 19:18
  • Can you show that either example doesn't produce what the original OP asked for? The programs ran here just as I would have expected. Read from stdin into a QByteArray until EOF is reached and then exit the application gracefully. One uses a task the other one avoids the event loop altogether. – Michael Petch Sep 08 '14 at 19:26
  • And to be specific. `main()` queues up a one shot event specified by task. `app.exec()` is executed. The only even in the queue is our task. QT executes the `run()` method. Task::Run() reads stdin until EOF and then emits a `finished` signal. `Finished` was tied to a slot that does quit(). Therefore the event loop is terminated. Control leaves app.exec and the program exits in return. If this example **blocks** (somewhere other reading stdin) then I'd like you to tell me where. I will admit the OP starting an event loop is useless for the example given. It is unnecessary overhead. – Michael Petch Sep 08 '14 at 19:40
  • Thanks for your answer. The focus of the question lies more on the fact that I'm not able to read from stdin till EOF and not on the whole Eventloop discussion. The first example provided by you seems to work on linux, if it works on windows as well I'll accept your answer (I'll have to test it). – Timo Sep 11 '14 at 16:02
  • Ah you know, when I coded my example it didn't even occur to me that you were using `readsome`. I just used what I know works. `readsome` isn't meant for reading from files (and stdin). It is meant for binary data on asynchronous sources usually. Technically `eof` doesn't get set with `readsome`. `read` is different in that regard. There is an SO article here that may be of interest [link](http://stackoverflow.com/questions/9191876/when-does-ifstreamreadsome-set-eofbit) – Michael Petch Sep 11 '14 at 16:17
  • I should point out that in your question the comment `Am I having a Event-Loop Problem?` made it ambiguous about this being an event loop problem. If you don't deal with the event loop or get rid of it you'll find that the program won't exit even if you do read from `stdin` properly. I ended up dealing with both. Sorry about that – Michael Petch Sep 11 '14 at 16:22
  • I have edited the code to deal with a difference on WIN32 platforms defaulting stdin and stdout to text mode rather than binary. Your example suggests you will be reading binary data so this will likely be necessary for you to do. – Michael Petch Sep 11 '14 at 18:36
  • You're totally right, It was unclear what my real question was. Furthermore I oversimplified it, sorry for that. I've edited the Question now. – Timo Sep 11 '14 at 18:42
  • Ah now it is a different question. I think the original question was answered. If I were you I'd revert the last edit and go back to the original question. Accept an answer here if it solves the problem that was asked and then create a totally new question. People coming to read this question/answer will find it confusing if you don't. – Michael Petch Sep 11 '14 at 18:47
  • 1
    I'll revert the question. Your "_setmode(_fileno(stdin), _O_BINARY);" solved the original question and the modified. Thanks a lot. – Timo Sep 11 '14 at 18:51
  • Great. I am going to amend my answer removing the Event loop sample that really didn't apply. – Michael Petch Sep 11 '14 at 18:53
  • extremely useful and concise code to implement a simple stdin receiver! any idea how this interacts with QProcess::closeWriteChannel() being called on the other (starter) end? – d.Candela Apr 03 '17 at 09:40