problem in reading binary file (mix of ascii and binary) - c++

My code for reading binary file is:
dataFile.open(fileName.c_str());
ifstream binData("Trafficlog_Data.txt", ios::in | ios::binary); //binary data file
if(!binData) {
cout << "Cannot open file.\n";
return -1;
}
char *memblock;int nBytes =12;
memblock = new char [nBytes+1];
binData.read(memblock,nBytes);
memblock[nBytes+1]='\0';
std::string message;message.assign(memblock,nBytes);
printf("%s\n",message.c_str());
Now i have given a file as input which contain binary and ascii data
RFB 003.003
RFB 003.003
--some binary data--
When I am reading first 12 bytes of file which is "RFB 003.003\n" but it prints "RFB 003.003=". Can anyone tell where i m getting it wrong please.
promblem is not with '\0'. problem is it is not reading "RFB 003.003\n" . IS it because this file is mix of binary and ascii data

You didn't allocate memory for memblock:
char *memblock = new char[nBytes+1];

Change:
memblock[nBytes+1]='\0';
to:
memblock[nBytes]='\0';
Let's say you read in six bytes to memblock, that goes into positions 0 through 5 inclusive:
0 1 2 3 4 5 6 7
+---+---+---+---+---+----+---+---+
| H | E | L | L | O | \n | ? | ? |
+---+---+---+---+---+----+---+---+
(the ? areas still contain whatever rubbish was there before).
You then need to put the null terminator at position 6 rather than position 7 as your code is doing.
By placing the null terminator too far to the "right", you're including that first ? position, which could be holding anything.
That's what's causing your specific problem. You also have an issue that you're not allocating space to hold the data you're reading in. You just have a char * but you're not actually initialising it to point to usable memory. That's almost certainly going to cause problems.
Probably the simplest solution is to define it as:
char memblock[nBytes+1];
Although I see you've fixed that in your question now so it's of little consequence. The actual problem (putting the null byte at the wrong location) is covered above.

You're off-by-one: just do memblock[nBytes]='\0'; The index starts at 0, so if nBytes is 0 you're writing at the first position, if nBytes is 1 you're writing at the second position and so on. By doing nBytes + 1 you actually jumped ahead one position and left one garbage byte at the end of the string.

Related

Moving through text file c++

I'm trying to save numbers from first txt file to second one in reversed order.
To be clear, inside 1st txt I have typed numbers from 1 to 10 (decimal notation). When I try to count them, I get 5 or 7, depending on what's between them (space or enter).
Then, another error is that inside 2nd txt program saves as much "0s" as dl's variable value is equal to instead of loaded numbers in reversed order.
I paste the whole code, because I don't know file operation rules good enough to determine which exact part could be the source of problem. Thank You in advance.
#include <fstream>
#include <iostream>
using namespace std;
int main() {
fstream plik1;
plik1.open("L8_F3_Z2a.txt", ios::in | ios::binary);
fstream plik2;
plik2.open("L8_F3_Z2b.txt", ios::out);
if(!plik1.good() || !plik2.good()) {
cout << "file(s) invalid" << endl;
return 1;
}
plik1.seekg(0, ios::end);
int dl = plik1.tellg() / sizeof(int);
cout << "length = " << dl << endl;
int a;
for(int i = 0; i < dl; i++) {
plik1.seekg((i + 1) * sizeof(int), ios::end);
plik1 >> a;
plik2 << a;
cout << i + 1 << ". a = " << a << endl;
}
plik1.close();
plik2.close();
return 0;
}
edit the output is:
length = 7
1. a = 0
2. a = 0
3. a = 0
4. a = 0
5. a = 0
6. a = 0
7. a = 0
--------------------------------
Process exited after 0.03841 seconds with return value 0
Press any key to continue . . .
Problem
When a file is encoded as text the binary size of the data is irrelevant.
int dl = plik1.tellg() / sizeof(int);
will get you the side of the file in integers, but the file isn't storing integers. It is storing a stream of characters. Say for example the file holds one number:
12345
which is five characters long. Assuming the file is using good ol ASCII, that's 5 bytes. When 12345 is converted to an int it will probably be 4 or 8 bytes and almost certainly not 5 bytes. Assuming the common 32 bit (4 byte) int
int dl = plik1.tellg() / sizeof(int);
int dl = 5 / 4;
int dl = 1;
Yay! It worked! But only by the grace of whatever deity or cosmic entity you worship. Or don't worship. I'm not going to judge. To show why you can't count on this, lets look at
123
this is three characters and 3 bytes, so
int dl = plik1.tellg() / sizeof(int);
int dl = 3 / 4;
int dl = 0;
Whoops.
Similarly
1 2 3 4 5
is five numbers. The file length will probably be the sum of one byte per digit and one byte per space, 9 bytes.
Where this gets weird is some systems, looking at you Windows, use a two character end of line marker, carriage return and a line feed. This means
1
2
3
4
5
will sum up to 13 bytes.
This is why you see a different size depending on whether the numbers are separated with spaces or newlines.
Solution
The only way to find out how many numbers are in the file is to read the file, convert the contents to numbers, and count the numbers as you find them.
How to do that:
int num;
int count = 0;
while (plik1 >> num) // read numbers until we can't read any more
{
count++;
}
From this you can determine the size of the array you need. Then you rewind the file, seek back to the beginning, allocate the array and read the file AGAIN into the array. This is dumb. File IO is painfully slow. You don't want to do it twice. You want to read the file once and store as you go without caring how many numbers are in the file.
Fortunately there are a number of tools built into C++ that do exactly that. I like std::vector
std::vector<int> nums;
int num;
while (plik1 >> num)
{
nums.push_back(num);
}
vector even keeps count for you.
Next you could
std::reverse(nums.begin(), nums.end());
and write the result back out.
for (int num: nums)
{
plik2 << num << ' ';
}
Documentation for std::reverse
If your instructor has a no vector policy, and unfortunately many do, your best bet is to write your own simple version of vector. There are many examples of how to do this already on Stack Overflow.
Addendum
In binary 5 integers will likely be 20 or 40 bytes no matter how many digits are used and no separators are required.
It sounds like storing data as binary is the bees knees, right? Like it's going to be much easier.
But it's not. Different computers and different compilers use different sizes for integers. All you are guaranteed is an int is at least 2 bytes and no larger than a long. All of the integer types could be exactly the same size at 64 bits. Blah. Worse, not all computers store integers in the same order. Because it's easier to do some operations if the number is stored backwards, guess what? Often the number is stored backwards. You have to be very, very careful with binary data and establish a data protocol (search term for more on this topic: Serialization) that defines the how the data is to be interpreted by everyone.

Why is the length of a string off by one on the first read of a file?

I am perplexed with the way my program is performing. I am looping the following process:
1) take the name of a course from an input file
2) output the length of the name of the course
The problem is that the first value is always one less than the actual value of the string.
My first string contains 13 characters (including the colon), but nameOfClass.length() returns 12. The next string, the character count is 16 and indeed, nameOfClass.length() returns 16.
Every value after that also returns the expected value, it is only the first that returns the expected value minus 1.
Here's the (reduced) code:
std::ifstream inf("courseNames.txt");
int numberOfClasses = 10;
string nameOfClass;
for (int i = 0; i < numberOfClasses; i++) {
std::getline(inf, nameOfClass,':');
std::cout << nameOfClass.length() << "\n";
}
The file looks like this (courseNames.txt):
Pre-Calculus:
Public-Speaking:
English I:
Calculus I:
...etc. (6 more classes)
This is what I get:
12
16
10
11
Can anyone explain this behavior of the .length() function?
You have a problem, but you have the wrong conclusion. std::getline reads but doesn't output the delimiter, and so the first result is indeed 12.
It also doesn't output the delimiter for any subsequent lines, so why is there always one more? Well, look what is after that :. That's right, a new line!
Pre-Calculus:
^ a new line
So your nameOfClass variable, except for the first string, always stores an extra newline before the other characters.
The fix is easy enough, just ignore the newline after reading the string.
inf.ignore(); // ignore one character
So, not the first result was wrong, it was the only one right :)

C++ Reading from file incorrectly

There is a certain problem that I would like to have help with. Here is how it goes:
I'm using the following code to read a certain amount of characters into a char array to process later:
char str[15]; // first 16 characters that i need to get from file
std::ifstream fin("example.txt");
if(fin.is_open()){
std::cout<<"File opened successfully \n";
for(int i = 0; i<=15; i++)
{
fin.get(str[i]); //reading one character from file to array
}
}
else{
std::cout<<"Failed to open file";
}
std::cout<<str;
It works fine for the first 4 or even 5 characters, but when it reaches to 8 it starts to print out garbage characters.
Contents of the example.txt file, from which i read text.
The Quick Brown Fox Jumped Over The Lazy Dog The Quick Brown Fox Jumped Over The Lazy Dog
Output when I read 8 characters:
The Quic�j�
Output when I read 16 characters:
The Quick Brown ASCII
Why is this happening and where did the 'ASCII' come from when I try to read certain length from a file?
And lastly, what kind of code should I utilize if I want to get specific lengths from a file? For example if I want to read the first 4 or 8 or 16 or even 20 characters? It doesn't necessarily have to be into a char array, it can be saved into a string.
Thank you in advance.
Your char array is only 15 chars long. So this line goes out of bounds:
for(int i = 0; i<=15; i++)
If i is equal to 15 that's one too many because your array counts from 0 to 14.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <= count them!
There are 15 places starting at 0 ending at 14
Also when strings are stored in memory they have to be terminated by a null character '\0'. Otherwise the function that prints them doesn't know when to stop, which is probably where your garbage is coming from.
So, because the null terminator takes one of your 15 spaces, that only leaves you with 14 to read from the file.
So:
for(int i = 0; i < 14; i++)
{
fin.get(str[i]); //reading 14 characters (0-13)
}
str[14] = '\0'; // add the string terminator at the end of the array.
See if that works.

Ifstream Read Useless Data

I'm using C++ to read a file in chunks.
The file contains integers, two per line.
First of all, I'm using this to find the file's length :
input.seekg (0, input.end);
int length = input.tellg();
input.seekg (0, input.beg);
After that, i check if length is bigger than chunksize, and if this is true, I allocate memory for the chunk...
char * buffer = new char [chunksize];
Ok, so here goes the reading function...
while (true)
{
input.read (buffer,chunksize);
cout<<buffer;
if(input.eof()) break;
}
Immediately after that I delete [] buffer;
However, I'm facing a problem with this code. For example, when the input file is like that :
2 5
4 5
6 8
7 5
4 2
1 2
The program will not output the expected characters, but something like :
2 5
4 5
6 8
7 5
4 2
1 2 2
1 2
Do you know the reason for these extra characters? If the file's size is less than chunksize, I input.read using its length and it works just fine. Maybe if using read with a length larger than the size of the file makes it not work correctly?
Thanks a lot
Your string is not NULL-terminated. The read() function will not put a '\0' at the end of what it reads for you, so when you go to print it, you're printing essentially garbage data beyond the end of what you read, because the print code is expecting a NULL terminator to mark the end of the string.

sorting through a file/returning info

Just need general project help.
Basically I need to do this for 8 players. The numbers come from a file im supposed to call in. The first 5 numbers for for the first 5 games, the next for rebounds, and then for blocks. Im assuming I need to call in a loop to read the first name, last name, points, rebounds and blocks, process that info and then output the information.Any tips/ suggestions?
ex from the text file:
Thomas Robinson 17 28 10 16 10 11 12 13 8 9 1 1 1 0 1
ex from what I'm supposed to return that information to
Game Log
-----------------------------------------------------------------------------
Player Name : Thomas Robinson
-----------------------------------------------------------------------------
Game # Points Rebounds Blocks
-----------------------------------------------------------------------------
1 17 11 1
2 28 12 1
3 10 13 1
4 16 8 0
5 10 9 1
-----------------------------------------------------------------------------
I think this is homework, but since I don't know which functions can be used, and which functions can't, my answers may be can't fit the request.
At a first look, I got three ideas.
1) using ifstream::get()
ifstream in_file;
in_file.open("your_file_name.txt");
char ch;
string str = "";
while(in_file.get() != '\n')
{
str = "";
while((ch = in_file.get()) != ' ')
{
// add ch to str.
str += string(&ch, 1);
}
// push str into an array, vector, stack, etc.
/*...*/
}
in_file.close();
2) read the line into a string, and then use a split function, you can find how to implement a split function everywhere.
3) use the ifstream::getline() function, it provides a delemiter parameter.
you can find the usage of ifstream::get() and ifstream::getline() here and here
The code I provide in 1) is probably not a good practice, you should check the 'EOF' stream error, in_file.open()'s exceptions etc.
btw, the code I first wrote was an error code, you can't use str += string(ch), you should either write str += string(&ch, 1) or str += string(1, ch) or str += ch you can find string's constructors here. Sorry for the error code again.
You can parse the file with the ">>" operator pretty nicely if everything is separated by spaces and newlines. Which is how the ">>" operator works. So, yes, you need a loop. Basically you want the loop to work like this:
(I never knew you could do this in Comp Sci 1. It would've saved me so much trouble...I used to do things like what the other answer is doing.)
(I'm also assuming you know how to open a txt file as an ifstream. If not, see http://www.cplusplus.com/reference/iostream/ifstream/open/.)
int temp;
int n = 0;
int x = 1;
while(textfile >> temp) // Each time through the loop, this will make temp
// the next value in the file. It will stop when
// there's nothing more to read.
{
/* Now it's going to go from left to right through the file, so you
need some logic to put it in the right place. you know that every
five numbers start a new column, so:*/
array[x][n] = temp; //Start x at 1 because you're skipping the first column
n++;
if (n == 5) {
n = 0;
x++; //Every five values, move on to the next column
}
Now your array will have the stuff where it needs to be. Just output it according to plan.