[c++] get() and read() function from fstream

Z

ZikO

Hi.

Im using C++ Compiler as below:
g++ (GCC) 4.3.0 20080305 (alpha-testing) mingw-20080502
Copyright (C) 2008 Free Software Foundation, Inc.

Im writing the simple program which is supposed to give info about sound
wave file. It checks whether a file is a wave format (4 ASCII bytes
should be like {'R','I','F','F'}) and then it reads what the size of the
file is (another 4 bytes). However, I found difficult to understand how
to use both get() and read() functions properly which are in <fstream>
library.

there is the code below in which I check if file starts with "RIFF". I
have used waveFile.get() function to obtain 4 bytes from the input
stream, although I wanted read() before. I had to use get() because if I
used read() instead the program would go straight to else branch and
display "This is not a wave file", even if it is.

Any suggestions why read() does not work here?

Thanks.



PS. The test file can be found here:
http://rapidshare.de/files/44749739/test.wav.html

Code:
..#include <iostream>
..#include <fstream>
..#include <cstring>
..
..using namespace std;
..
..void checkStream(ifstream& in_str) {
..  // if() {} ...
..  return;
..}
..
..int main() {
..  ifstream waveFile("test.txt");
..  int filePointer = 0;
..  char temp[5] = {0,0,0,0};
..  if(waveFile.get(temp,4)) {
..    // cout << "stream pointer = " << waveFile.tellg() << endl;
..    filePointer += 4;
..    char waveChunk[] = {"RIFF"};
..    if(strcmp(temp, waveChunk)) {
..      cout << "This is a wave file." << endl;
..    }
..    else {
..      cout << "This is not a wave file" << endl;
..      return 0;
..    }
..  }
..  else
..    checkStream(waveFile);
..
..  int roz;
..  waveFile.seekg(filePointer);
..  if(waveFile.read(reinterpret_cast<char*>(&roz), 4)) {
..    filePointer += 4;
..    roz += 8;
..    cout << "The size is: " << roz << endl;
..  }
..  else
..  checkStream(waveFile);
..}
 
J

James Kanze

Im using C++ Compiler as below:
g++ (GCC) 4.3.0 20080305 (alpha-testing) mingw-20080502
Copyright (C) 2008 Free Software Foundation, Inc.
Im writing the simple program which is supposed to give info
about sound wave file. It checks whether a file is a wave
format (4 ASCII bytes should be like {'R','I','F','F'}) and
then it reads what the size of the file is (another 4 bytes).
However, I found difficult to understand how to use both get()
and read() functions properly which are in <fstream> library.
there is the code below in which I check if file starts with
"RIFF". I have used waveFile.get() function to obtain 4 bytes
from the input stream, although I wanted read() before. I had
to use get() because if I used read() instead the program
would go straight to else branch and display "This is not a
wave file", even if it is.
Any suggestions why read() does not work here?

Not really, but there are a number of problems in your code.
Code:
.#include <iostream>
.#include <fstream>
.#include <cstring>[/QUOTE]
[QUOTE]
.using namespace std;[/QUOTE]
[QUOTE]
.void checkStream(ifstream& in_str) {
.  // if() {} ...
.  return;
.}[/QUOTE]
[QUOTE]
.int main() {
.  ifstream waveFile("test.txt");[/QUOTE]

The wave format contains binary data, so you must open the file
in binary mode.  Otherwise, your code will only work on Unix and
Unix look-alikes (where binary and text modes are identical).

    std::ifstream waveFile( "test.txt", std::ios::binary ) ;

(Also, .txt is a very strange filename ending for a binary file.
If you're dealing with wave files, I'd expect something like
..wav.)

And are you sure the open succeeded.  You need to test the
status immediately:

    if ( ! waveFile ) {
        fatalError( "cannot open test.txt" ) ;
    }
[QUOTE]
.  int filePointer = 0;[/QUOTE]

Almost certainly not a problem in your tests, but int is
generallly not big enough to hold a position in a file.  I'd use
either std::streampos or long long.
[QUOTE]
.  char temp[5] = {0,0,0,0};[/QUOTE]

Just {} would be sufficient.  Interestingly enough the only byte
you really need initialized is the last, which is the only one
you don't initialize explicitly (but as soon as the initializer
list is present, all elements without an explicit initializer
are initialized as 0.
[QUOTE]
.  if(waveFile.get(temp,4)) {
.    // cout << "stream pointer = " << waveFile.tellg() << endl;
.    filePointer += 4;
.    char waveChunk[] = {"RIFF"};
.    if(strcmp(temp, waveChunk)) {[/QUOTE]

What's wrong with just strcmp( temp, "RIFF" ) ?
[QUOTE]
.      cout << "This is a wave file." << endl;
.    }
.    else {
.      cout << "This is not a wave file" << endl;
.      return 0;
.    }
.  }
.  else
.    checkStream(waveFile);[/QUOTE]
[QUOTE]
.  int roz;
.  waveFile.seekg(filePointer);
.  if(waveFile.read(reinterpret_cast<char*>(&roz), 4)) {[/QUOTE]

And this won't necessarily work.  You can't just read raw binary
data.  You have a binary file with a specific format.  That
format isn't directly supported by the standard, so you have to
implement it yourself.

From what little information I've found, you need to do
something like:

    unsigned long
    readWaveInt(
        std::istream&   source )
    {
        unsigned long   result = 0 ;
        for ( int shift = 0 ; shift != 32 ; shift += 8 ) {
            result |= source.get() << shift ;
        }
        return result ;
    }

(This will return some random value if you encounter EOF within
the function, but in this case, the call to get should set
failbit, so you can detect the error by testing source.)

Note that this is *not* the usual external format for an int.
[QUOTE]
.    filePointer += 4;
.    roz += 8;
.    cout << "The size is: " << roz << endl;
.  }
.  else
.  checkStream(waveFile);
.}

If this code outputs "This is not a wave file", either you've
not successfully opened the file, or the first four bytes aren't
"RIFF" (or you're on a platform where the native character
encoding isn't an extension of ASCII, but that's highly
unlikely---I don't think you'd be reading wave files on a
mainframe).
 
Z

ZikO

James said:
. char temp[5] = {0,0,0,0};

It's funny. Just missed last 0, the strange think is the compiler did
not warn that, however,
char temp[5];
works perfectly. Thnx.
What's wrong with just strcmp( temp, "RIFF" ) ?
Nothing.

From what little information I've found, you need to do
something like:

unsigned long
readWaveInt(
std::istream& source )
{
unsigned long result = 0 ;
for ( int shift = 0 ; shift != 32 ; shift += 8 ) {
result |= source.get() << shift ;
}
return result ;
}

I like this idea. Probably will help me avoid many problems. Thanks.

If this code outputs "This is not a wave file", either you've
not successfully opened the file, or the first four bytes aren't
"RIFF" (or you're on a platform where the native character
encoding isn't an extension of ASCII, but that's highly
unlikely---I don't think you'd be reading wave files on a
mainframe).

The problem is very trivial ^^. strcmp() returns 0 if strings are equal.
Unfortunately, 0 means false in if() statement which I didnt know. I was
sue strcmp would return bool true/false. This is another reason to stop
using old <cstring>

Thanks for the constructive suggestions =)
 
J

James Kanze

James said:
. char temp[5] = {0,0,0,0};
It's funny. Just missed last 0, the strange think is the
compiler did not warn that,

Not really. The standard says if there is an initializer,
anything left over without initialization is zero initialized.
So you can write things like:

int array[ 1000 ] = {} ;

and get the entire array initialized. And I certainly don't
want to have to write out 1000 0's just to avoid a warning.
(G++ does warn, and it gets on my nerves.)
however,
char temp[5];
works perfectly.

Only if it's static, or you do something to force the last byte
to 0. (Some compilers do zero the memory. Others do so only in
debugging mode.)

[...]
I like this idea. Probably will help me avoid many problems.

In the end, it's the only thing that works portably.

Since wave files were designed by Microsoft, for Intel based
processors, just reading the raw bytes into the int will work
under Windows. It won't work on the machine I usually work on,
nor on a lot of other machines.
The problem is very trivial ^^. strcmp() returns 0 if strings
are equal. Unfortunately, 0 means false in if() statement
which I didnt know. I was sue strcmp would return bool
true/false. This is another reason to stop using old <cstring>

I saw that. It's funny I didn't spot it myself, but in my
experience, the only people who use char[] and <string.h> are
ex-C hackers, who generally get these sort of things right, so I
didn't look too carefully.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

[c++] get() and read() functions. 17
eof error using '>>' in fstream 2
fstream Buffers 26
fstream File i/o 1
fstream and ofstream 2
problem with fstream 3
fstream, getline() and failbit 15
fstream tests 3

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top