Why do I have do use the Seekg()-function two times in a row to make it work?

Code:

fbin.seekg(0); //jump to record at start of file
	fbin.seekg(0); //jump to record a second time (else it won't work)
	fbin.read(name, sizeof(name)); //read from data field 1
	fbin.read(reinterpret_cast<char*>(&age), sizeof(int)); //read from data field 2

	cout << "Name: " << name << endl;
	cout << "Age: " << age;

Edited 6 Years Ago by Nick Evan: Added code-tags

Without seeing a full example, it seems odd behaviour.

As a first thought, I assume that you are talking about opening a file that is acting as a database and you want to move to the first entry.

you might try explicitly adding the ios::beg

fbin.seekg(0, std::ios::beg);

if this doesn't achieve anything we will need more information as to file format and where you opened the file.

P.S. it is helpful to use code tags

Why do I have do use the Seekg()-function two times in a row to make it work?

Code:

fbin.seekg(0); //jump to record at start of file
	fbin.seekg(0); //jump to record a second time (else it won't work)
	fbin.read(name, sizeof(name)); //read from data field 1
	fbin.read(reinterpret_cast<char*>(&age), sizeof(int)); //read from data field 2

	cout << "Name: " << name << endl;
	cout << "Age: " << age;

Without seeing a full example, it seems odd behaviour.

As a first thought, I assume that you are talking about opening a file that is acting as a database and you want to move to the first entry.

you might try explicitly adding the ios::beg

fbin.seekg(0, std::ios::beg);

if this doesn't achieve anything we will need more information as to file format and where you opened the file.

P.S. it is helpful to use code tags

Thank you for answer and sorry for little information. Here's my full code:

#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;

int get_int(int def); //returns text-input buffer in int format
void write_record(); //writes a record
void read_record(); //reads a record
void change_filepath(); //changes the filepath
int get_used_records(); //returns number of used records

char * tasklist[3] = { //list of tasks
	"Write to record",
	"Read from record",
	"Change filepath"
};
char name[20]; //data field 1 in record
int age; //data field 2 in record
int n; //current record
int recSize = sizeof(name) + sizeof(int); //size of a record (name + age = 24 byte)
char filepath[100] = "C:\\Users\\Pelle\\Desktop\\file"; //default filepath
fstream fbin(filepath, ios::binary | ios::in | ios::out); //open file for binary reading and writing

int main() {
	int task = 0; //task to perform
	
	//check for errors when opening file
	if (!fbin) {
		cout << "Error!" << "could not open file " << filepath;
		return -1;
	}

	while (task != 4) {
		//ask for task to perform and display important variables
		cout << "Current filepath: " << filepath << endl;
		cout << "Number of used records: " << get_used_records() << endl;
		for (int i = 0; i < sizeof(tasklist) / 4; i++)
			cout << i+1 << ": " << tasklist[i] << endl;
		cout << "Task to perform (4 to quit): ";
		task = get_int(1);

		//perform the right task
		switch (task) {
			case 1:
				write_record();
				break;
			case 2:
				read_record();
				break;
			case 3:
				change_filepath();
				break;
		}
		cout << endl;
	}



	//close file
	fbin.close();
	
	return 0;
}

int get_int(int def) {
	char str[10];
	cin.getline(str, 9);
	if (strlen(str) == 0)
		return def;
	else
		return atoi(str);
}

void write_record() {
	cout << "Enter record number: "; //ask for record
	n = get_int(0);
	cout << "Enter name: "; //ask for data field 1
	cin.getline(name, 19);
	cout << "Enter age: "; //ask for data field 2
	age = get_int(0);

	fbin.seekp(recSize * n); //jump to record
	fbin.write(name, sizeof(name)); //write to data field 1
	fbin.write(reinterpret_cast<char*>(&age), sizeof(int)); //write to data field 2
	cout << "Record has been altered successfully!" << endl;
}

void read_record() {	
	cout << "Enter record number: "; //ask for record
	n = get_int(0);
	fbin.seekg(0);
	fbin.seekg(recSize * n); //jump to record
	fbin.read(name, sizeof(name)); //read from data field 1
	fbin.read(reinterpret_cast<char*>(&age), sizeof(int)); //read from data field 2

	cout << "Name: " << name << endl;
	cout << "Age: " << age;
}
void change_filepath() {
	cout << "Enter filepath: ";
	cin.getline(filepath,  99);
	fbin.close();
	fstream fbin(filepath, ios::binary | ios::in | ios::out); //open new file for binary reading and writing
}
int get_used_records() {
	char temp[20];
	int counter = 0;
	for (int i = 0; !fbin.eof(); i++) {
		fbin.seekg(recSize * i);
		fbin.read(temp, sizeof(name));
		if (strlen(temp) != 0)
			counter++;
	}	
	return counter;
}

There are probably lots of other programming faults as well, because I can't get it to work properly. I'm still a beginner :)

It might be an idea to use ifstream and ostream and open and close the file as needed.

I have looked at your code and haven't spotted the mistake. seekg(pos) should be setting an offset. However, I'm not sure whether the reason that you are having to move to the begining of the file first might be because the file is still open for writing.

What I suggested before is make line 92:

fbin.seekg(recSize * n, ios::beg);

without line 91
this says to use an offset from the explicit position start

the seekg() returns a value that might be showing an error.

It might be an idea to use ifstream and ostream and open and close the file as needed.

I have looked at your code and haven't spotted the mistake. seekg(pos) should be setting an offset. However, I'm not sure whether the reason that you are having to move to the begining of the file first might be because the file is still open for writing.

What I suggested before is make line 92:

fbin.seekg(recSize * n, ios::beg);

without line 91
this says to use an offset from the explicit position start

the seekg() returns a value that might be showing an error.

I tried to add ios::beg to the function, but it gave no result. I actually though that the seekg and seekp functions set the absolute position in the file stream, but maybe I'm wrong.

I tried to add ios::beg to the function, but it gave no result. I actually though that the seekg and seekp functions set the absolute position in the file stream, but maybe I'm wrong.

They should do but there was a small chance of an overflow error.

I'm not sure what is happening - I assume that you are checking that n is meaningful.

it might be worth checking the return for all the methods
ie.
http://http://www.cplusplus.com/reference/iostream/istream/read/

and that the file is_open();

They should do but there was a small chance of an overflow error.

I'm not sure what is happening - I assume that you are checking that n is meaningful.

it might be worth checking the return for all the methods
ie.
http://http://www.cplusplus.com/reference/iostream/istream/read/

and that the file is_open();

The function that creates the error is int get_used_records(), but I don't understand why. As long as i write fbin.seekg(recSize * n, ios::beg), it shouldn't be of any matter. I checked if the file was open, and it was, but how do I check the return values? Do I have to use a try/catch block?

Ok, I am a little confused I assumed that you were having a problem with the read.

Now several of your methods, are using approaches different to what I would choose to do.

When you say an error do you mean that the code crashes or just doesn't return the value you were expecting.

You might have a problem with !fbin.eof() this can lead to issues but I can't remember precisely why.

It is possible that your for loop is going beyond the end of your file.
You would probably be best off stripping out the open of binary files and stick to ascii as it is easier to maintain, until the code is working.

To find the records - get the size of the file and divide by RecSize would be one option


I am assuming that you are not calling option change_path
does your file_name not have an extension ie ".txt"

for test purposes add the following:

int get_used_records() {
  char temp[20];
  int counter = 0;
  for (int i = 0; !fbin.eof(); i++) 
  {
      std::cout << "i == " << i << " " << counter << std::endl;
      fbin.seekg(recSize * i);
      fbin.read(temp, sizeof(name));
      if (strlen(temp) != 0)
      {
               std::cout << "temp = " << temp << std::endl;
	counter++;	
      }
return counter;
}

and set task to 4

Line 37: sizeof(task_list) / 4 doesn't look pretty

The other question is what exactly is your input file structure
i have similar tasks that I have done before using ifstream , getline() and ascii files

The function that creates the error is int get_used_records(), but I don't understand why. As long as i write fbin.seekg(recSize * n, ios::beg), it shouldn't be of any matter. I checked if the file was open, and it was, but how do I check the return values? Do I have to use a try/catch block?

Ok, I am a little confused I assumed that you were having a problem with the read.

Now several of your methods, are using approaches different to what I would choose to do.

When you say an error do you mean that the code crashes or just doesn't return the value you were expecting.

You might have a problem with !fbin.eof() this can lead to issues but I can't remember precisely why.

It is possible that your for loop is going beyond the end of your file.
You would probably be best off stripping out the open of binary files and stick to ascii as it is easier to maintain, until the code is working.

To find the records - get the size of the file and divide by RecSize would be one option


I am assuming that you are not calling option change_path
does your file_name not have an extension ie ".txt"

for test purposes add the following:

int get_used_records() {
  char temp[20];
  int counter = 0;
  for (int i = 0; !fbin.eof(); i++) 
  {
      std::cout << "i == " << i << " " << counter << std::endl;
      fbin.seekg(recSize * i);
      fbin.read(temp, sizeof(name));
      if (strlen(temp) != 0)
      {
               std::cout << "temp = " << temp << std::endl;
	counter++;	
      }
return counter;
}

and set task to 4

Line 37: sizeof(task_list) / 4 doesn't look pretty

The other question is what exactly is your input file structure
i have similar tasks that I have done before using ifstream , getline() and ascii files

I know that your way of resolving common programming tasks are a whole lot better than mine. And that is exactly why I posted this code here on the forum, so that I could get tips on how to be a better programmer.

The behaviour of the seekg and seekp functions is still a mystery for me, but I made it work any way.

The reason I didn't calculate the number of records by dividing file size by recSize, was because that would give the wrong result. Suppose record 0, 1, 2, 3 and 5 were full, but not 4. Then it wouldn't calculate right.

#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;

int get_int(int def); //returns text-input buffer in int format
void write_record(); //writes a record
void read_record(); //reads a record
int get_used_records(); //returns number of used records
void print_all_records(); //prints a list of all records

char * tasklist[4] = { //list of tasks
	"Write to record",
	"Read from record",
	"Print all records",
	"Exit"
};
char name[20]; //data field 1
int age; //data field 2
int n; //current record
int recSize = sizeof(name) + sizeof(int); //size of a record (name + age = 24 byte)
char filepath[100] = "file"; //filepath
fstream fbin(filepath, ios::binary | ios::in | ios::out); //open file for binary reading and writing

int main() {
	int task = 0; //task to perform
	
	//check for errors when opening file
	if (!fbin) {
		cout << "Error!" << "could not open file " << filepath;
		return -1;
	}

	while (task != 4) {
		//ask for task to perform and display some info
		cout << "Current filepath: " << filepath << endl;
		cout << "Number of used records: " << get_used_records() << endl;

		for (int i = 0; i < 4; i++)
			cout << i+1 << ": " << tasklist[i] << endl;
		cout << "Task to perform (4 to quit): ";
		task = get_int(1);

		//perform the right task
		switch (task) {
			case 1:
				write_record();
				break;
			case 2:
				read_record();
				break;
			case 3:
				print_all_records();
				break;
		}
		cout << endl;
	}



	//close file
	fbin.close();
	
	return 0;
}

int get_int(int def) {
	char str[10];
	cin.getline(str, 9);
	if (strlen(str) == 0)
		return def;
	else
		return atoi(str);
}

void write_record() {
	cout << "Enter record number: "; //ask for record
	n = get_int(0);
	cout << "Enter name: "; //ask for data field 1
	cin.getline(name, 19);
	cout << "Enter age: "; //ask for data field 2
	age = get_int(0);

	fbin.seekp(recSize * n, ios::beg); //jump to record
	fbin.write(name, sizeof(name)); //write to data field 1
	fbin.write(reinterpret_cast<char*>(&age), sizeof(int)); //write to data field 2
	cout << "Record has been altered successfully!" << endl;
}

void read_record() {	
	cout << "Enter record number: "; //ask for record
	n = get_int(0);

	fbin.seekg(recSize * n); //jump to record

	fbin.read(name, sizeof(name)); //read from data field 1
	fbin.read(reinterpret_cast<char*>(&age), sizeof(int)); //read from data field 2
	if (fbin.eof() || strlen(name) == 0 && age ==0) { //check if record is empty
		cout << "Record is empty" << endl; 
		return;
	}

	cout << "Name: " << name << endl;
	cout << "Age: " << age << endl;
}
int get_used_records() {
	char temp[24];
	int counter = 0;

	for (int i = 0; !fbin.eof(); i++) {
		fbin.seekg(recSize * i, ios::beg);
		fbin.read(temp, sizeof(temp));
		if (fbin.eof()) break;
		if (strlen(temp) != 0) counter++;
	}
	fbin.seekg(0); //reset reading position
	return counter;
}
void print_all_records() {
	cout << endl;
	for (int i = 0; i < 20; i++) {
		fbin.seekg(recSize * i);
		fbin.read(name, sizeof(name));
		fbin.read(reinterpret_cast<char*>(&age), sizeof(int));
		if (fbin.eof()) break;

		if (strlen(name) == 0 && age == 0)
			cout << "<empty>" << endl;
		else
			cout << name << " " << age << endl;
	}
	cout << endl;
}
This question has already been answered. Start a new discussion instead.