Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Read some bytes from binary file with qfile



  • Hi,
    I'm trying to get familiar with QFile. I need to read some bytes from binary file. Here is my code:

    QString myFile[]{"C:/Qt_Projects/raw_le.bin"};
    quint16_le dataFormatCode_le;
    qFile = new QFile(myFile);
     if (!qFile->open(QIODevice::ReadOnly)) {
        std::cout << "QFile: Error opening file!" << std::endl;
        return;
    }
    qFile->seek(3224);
    qFile->read(dataFormatCode_le, 2); // error
    

    I know that 3225-3226 bytes is of qint16 format so I want to read these bytes in dataFormatCode_le variable but I don't understand how.
    I use Windows 10 x64, MSVC 64, Qt 5.14.0



  • @Please_Help_me_D
    Yes, I thought so! That's why it's good for both you & us to copy the error message here and examine it together!

    That read() overload requires a first parameter of type char *. You are passing your quint16_le dataFormatCode_le, which is not a pointer in the first place and is an integer not a char type in the second place.

    You have to pass the address of the area you want to read() into, so that read() has the place to store the data. This would be a start:

    qFile->read(&dataFormatCode_le, 2);
    

    That & is the magic. It passes the address of dataFormatCode_le, so that is where read() will put its bytes/chars, which is a good start!

    Now however the compiler will complain that it wants a char * but you have given it a quint16_le *, so it still won't be happy!

    This is where my knowledge of C++ gets a touch wobbly. I believe you'll want:

    qFile->read(reinterpret_cast<char *>(&dataFormatCode_le), 2);
    

    but experts may correct me on this. Give it a try!

    EDIT I see while I was typing @Konstantin-Tokarev has chimed in and talked about "endianness". What he says is correct, but I was trying to spare you that, assuming you only have one endianness at issue. What I have shown you is the equivalent using QFile & C++ of the code you have now shown using fopen() & fread().



  • @Please_Help_me_D
    When asking a question, please say what // error means, don't you think that helps or do you prefer we guess?

    If you are trying to use qint64 QIODevice::read(char *data, qint64 maxSize) for your qFile->read(dataFormatCode_le, 2), yours won't compile, is that your issue?



  • @JonB sorry, here is the error:
    *readsegy.cpp:37:16: error: no matching member function for call to 'read'
    qiodevice.h:122:12: note: candidate function not viable: no known conversion from 'quint16_le' (aka 'QSpecialInteger<QLittleEndianStorageType<unsigned short> >') to 'char ' for 1st argument
    qiodevice.h:123:16: note: candidate function not viable: requires single argument 'maxlen', but 2 arguments were provided

    Yes I can't compile it and this is my problem.
    I think that according to the documentation I shoud use QByteArray for this purpose but I have not used it before.



  • With QIODevice::read you can read only raw bytes (as char* or QByteArray), and then it's your responsibility to convert bytes to qint16 with correct endianness



  • @Konstantin-Tokarev thank you for the answer.
    Could you please give me an example how use QByteArray in my case?
    Also how do yo think, recently I used fread/fseek for these purposes and I coud use it to read data in struct such as:

    struct binHdr_struct {
        qint32 var1, var2;
        quint16 var3, var4;
    };
    
    void main(){
        binHdr_struct binHdr;
    
        pFile = fopen(myFile, "rb");
        if (pFile == nullptr){
          std::cout << "fopen: Error opening file!" << std::endl;
          return;
        }
        fseek (pFile , 3200 , SEEK_SET);
        fread(&binHdr, sizeof(binHdr_struct), 1, pFile);}
    

    So it was very simple to read data in struct. Is it possible to do something simple with QFile?
    Here my struct contain only 4 variables (var1,2,3,4) but actually it has over 30 of them



  • @Please_Help_me_D
    Yes, I thought so! That's why it's good for both you & us to copy the error message here and examine it together!

    That read() overload requires a first parameter of type char *. You are passing your quint16_le dataFormatCode_le, which is not a pointer in the first place and is an integer not a char type in the second place.

    You have to pass the address of the area you want to read() into, so that read() has the place to store the data. This would be a start:

    qFile->read(&dataFormatCode_le, 2);
    

    That & is the magic. It passes the address of dataFormatCode_le, so that is where read() will put its bytes/chars, which is a good start!

    Now however the compiler will complain that it wants a char * but you have given it a quint16_le *, so it still won't be happy!

    This is where my knowledge of C++ gets a touch wobbly. I believe you'll want:

    qFile->read(reinterpret_cast<char *>(&dataFormatCode_le), 2);
    

    but experts may correct me on this. Give it a try!

    EDIT I see while I was typing @Konstantin-Tokarev has chimed in and talked about "endianness". What he says is correct, but I was trying to spare you that, assuming you only have one endianness at issue. What I have shown you is the equivalent using QFile & C++ of the code you have now shown using fopen() & fread().



  • @JonB thank you!
    That was exactly what I was looking for
    By the way you approach also works with struct as I asked one message above



  • @JonB said in Read some bytes from binary file with qfile:

    EDIT I see while I was typing @Konstantin-Tokarev has chimed in and talked about "endianness". What he says is correct, but I was trying to spare you that, assuming you only have one endianness at issue. What I have shown you is the equivalent using QFile & C++ of the code you have now shown using fopen() & fread().

    "Sparing" someone from endianness when dealing with multibyte data types coming from files or network is effectively an advice to write broken code.



  • @Konstantin-Tokarev
    IMO, when the user still needs to understand how to take address to get a pointer and match method parameter types, that is what they need to grasp first. I do not advise people to write broken code, here I gave advice on how to action a compilation error.

    I never said endianness would not be covered. Had the user tried to get the code compiling correctly first, I would then have gone on to discuss the endianness issue and hence that he needed to change approach.

    I totally respect your point. Evidently you & I differ on how best to help a beginner.

    @Please_Help_me_D , or anyone else reading this in future
    Let's be absolutely clear. The code as shown will work in a limited set or circumstances on one architecture. @Konstantin-Tokarev is absolutely correct that to get this robust the approach should be changed to deal with integers/endiainness.



  • @Konstantin-Tokarev @JonB If I understood the comment then I use some if statement to define endianess. The idea is that I know what number should be in 3225-3226 bytes and I read in both quint16_le dataFormatCode_le and quint16_be dataFormatCode_be. Then compare these numbers and define the endianess.
    I just tried my updated with @JonB help code with both big- and little-endian files and it works
    By the way instead of reinterpret_cast<char >(&dataFormatCode_le) I use util::bit_cast<char>(&dataFormatCode_le)
    So I hope it should run on different architectures

    How to highlight text with red in this forum? Like @JonB did



  • @Please_Help_me_D
    I won't say much more, you had better get @Konstantin-Tokarev or another to suggest the correct way to do this here, including any necessary correct C++ casting. But

    read in both quint16_le dataFormatCode_le and quint16_be dataFormatCode_be. Then compare these numbers and define the endianess.

    does not sound right. If you mean you (somehow) read the data into those two types of variables, that does not sound right, and how can you "compare" and know which one is correct?

    You have to start by saying/discovering how that integer was stored into the file by the writer in the first place? Only then can you know how to read it back.



  • @JonB ok, thank you for advice.
    There is official description of the file format that tells us that number in 3225-3226 bytes should be in range from 1 to 5. I believe that there is no way that quint16_le dataFormatCode_le and quint16_be dataFormatCode_be be in this range at the same time. Only one of them should be in range.
    I have not used QByteArray before so I will postpone it for some time.



  • @Please_Help_me_D
    OK, but in case someone observes that this is not the way to do it (which they are entitled to do!), be aware of that.

    Given:

    There is official description of the file format that tells us that number in 3225-3226 bytes should be in range from 1 to 5.

    somewhere in such an official description there should also be a statement as to how they store 2-byte/16-bit numbers!


Log in to reply