seekg not working in binary mode

hey all, below is a snippet of my code in which it loops through a file trying to find the matching record of a selection given by the user. I used seekg() to start at the beginning of the file and loop through sequentially, but it is not starting at the beginning

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

//hardware.cpp

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <iomanip>
#include "hardware.h"

using namespace std;

void Hardware::updateExistingRecord(ofstream &addRecordInfo, ifstream &updateRecord, Hardware &_records)
{
	cout << "Enter record number you would like to update\n";
	cout << "Enter zero to exit\n?";
	std::cin >> selection;
	//tests if record exists
        isRecord = false;
	while (!isRecord)
	{
		updateRecord.clear();
		updateRecord.seekg(0, ios::beg);
		updateRecord >> recordNumber >> toolName >> quantity >> cost;
		while (!updateRecord.eof())
		{		
			//if record does not exist, will prompt user for another choice
			if (isRightRecord(selection, recordNumber))
			{
				isRecord = true;		//record exists, can stop searching
				break;
			}
			updateRecord >> recordNumber >> toolName >> quantity >> cost;
		}
		//if record does not exist
		if (isRecord == false)
		{
			cerr << "Error: Record does not exist\n"
				<< "Choose another record number\n"
				<< "Enter zero to exit\n?";
			std::cin.clear();
			std::cin >> selection;
		}
	}
//{...} Rest of function below 


it is a binary file:
 
ifstream inRecord("inventory.txt", ios::in | ios::binary);
Last edited on
but it is not starting at the beginning
How do you know, did you use tellg() to check?

It's not a good idea to loop on eof() like this:
 
while (!updateRecord.eof())
as there could be other errors which occur meaning eof is never reached.

A better alternative would be simply:
 
while (updateRecord)
Well it is weird... I used tellg() to check and it showed it started at the beginning, but it would just keep looping between

 
while (!updateRecord.eof())

and

 
if (isRightRecord(selection, recordNumber))


it would stay at the last record and just keep looping.
So I gathered that it did in fact start at the beginning, but then jumped to the last record and didn't end the loop
Have you solved this now? It's hard to offer further help without knowing the types of the variables recordNumber, toolName, quantity and cost (though i could make a guess at int, std::string, int and double), and importantly, seeing a sample of the contents of the file.
No, apologies for the delay. I changed it to

while (updateRecord)

two issues I'm currently having:
1). After the test data is entered, it displays 2 copies of the last entry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//test data automatically entered in file
	//arrays to store test data
	const int num = 8;		//number of entries
	Hardware records;		//construtor zeros out each data member
	int recordArray[num] = { 3, 17, 24, 39, 56, 68, 77, 83 };
	string toolNameArray[num] = { "Electric sander     ", "Hammer", "Jig saw", "Lawn mower",
		"Power saw", "Screwdriver", "Sledge hammer", "Wrench" };
	int quantityArray[num] = { 7, 76, 21, 3, 18, 106, 11, 34 };
	double costArray[num] = { 57.98, 11.99, 11.00, 79.50, 99.99, 6.99, 21.50, 7.50 };
	//inputting test data to file
	for (int i = 0; i < num; ++i)
	{
		records.setRecordNumber(recordArray[i]);
		records.setToolName(toolNameArray[i]);
		records.setQuantity(quantityArray[i]);
		records.setCost(costArray[i]);
		//seek position to input data
		outRecord.seekp((records.getRecordNumber() - 1) * sizeof(Hardware));
		//write user input into file
		outRecord.write(reinterpret_cast<const char*>(&records), sizeof(records));
	}


2). the location being read by the first post is in fact 0 (beginning of file), but while debugging, the recordNumber is shown as starting at the last one (i.e. 77), and therefore will not see when I enter 24 for example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
void Hardware::updateExistingRecord(ofstream &addRecordInfo, ifstream &updateRecord, Hardware &_records)
{
        //selection on which field to update
	enum Field { RECORD_NUMBER = 1, TOOL_NAME, QUANTITY, COST, EXIT };
	cout << "Enter record number you would like to update\n";
	cout << "Enter zero to exit\n?";
	std::cin >> selection;
	//tests if record exists
	while (!isRecord)
	{
		//***********************************
		//change position in all functions
		//***********************************
		updateRecord.clear();
		updateRecord.seekg(0, ios::beg);
		int location = updateRecord.tellg();
		cout << "LOCATION = " << location << endl;
		updateRecord >> recordNumber >> toolName >> quantity >> cost;
		//will exit
		if (selection == EXIT)
			break;
		while (updateRecord)
		{		
			cout << recordNumber << " " << toolName << " " << quantity << " " << cost << endl;
			//if record does not exist, will prompt user for another choice
			if (isRightRecord(selection, recordNumber))
			{
				isRecord = true;		//record exists, can stop searching
				break;
			}
			updateRecord >> recordNumber >> toolName >> quantity >> cost;
		}
		//if record does not exist
		if (isRecord == false)
		{
			cerr << "Error: Record does not exist\n"
				<< "Choose another record number\n"
				<< "Enter zero to exit\n?";
			std::cin.clear();
			std::cin >> selection;
		}
	}
	//reset file
	updateRecord.clear();
	updateRecord.seekg(0, ios::beg);
	while (!updateRecord.eof())
	{
		//read info from file
		updateRecord >> recordNumber >> toolName >> quantity >> cost;
		//if selection matches record number, ask user which field to update
		if (isRightRecord(selection, recordNumber))
		{
			//prints out the individual record that matches
			printTable();
			printData(recordNumber, toolName, quantity, cost);

			cout << "Enter which field you would like to update\n"
				<< "1- Record number\n"
				<< "2- Tool name\n"
				<< "3- Quantity\n"
				<< "4- Cost\n"
				<< "5- Exit\n?";
			switch (selection)
			{
				case RECORD_NUMBER:
					int tempRecordNumber;		//test to see if recordNumber is taken already
					cout << "Enter new record number\n?";
					std::cin >> tempRecordNumber;
					//search file to make sure existing record number does not exist
					updateRecord.clear();
					updateRecord.seekg(0, ios::beg);
					while (!updateRecord.eof())
					{
						//read info from file
						updateRecord >> recordNumber >> toolName >> quantity >> cost;
						//if record already exists
						if (isRightRecord(selection, recordNumber))
						{
							cerr << "Error: Record already exists\n";
							alreadyExists = true;
							break;
						}
					}
					//exit to make another selection
					if (alreadyExists)
						break;
					//record does not exist, update record number
					setRecordNumber(tempRecordNumber);
					//seek position to input data
					addRecordInfo.seekp((getRecordNumber() - 1) * sizeof(Hardware));
					//write user input into file
					addRecordInfo.write(reinterpret_cast<const char*>(&_records), sizeof(_records));
					break;
				case TOOL_NAME:
					cout << "Enter new tool name\n?";
					std::cin >> toolName;
					//updates toolName
					setToolName(toolName);
					//seek position to input data
					addRecordInfo.seekp((getRecordNumber() - 1) * sizeof(Hardware));
					//write user input into file
					addRecordInfo.write(reinterpret_cast<const char*>(&_records), sizeof(_records));
					break;
				case QUANTITY:
					cout << "Enter new quantity\n?";
					std::cin >> quantity;
					//if entered less than 0
					while (quantity < 0)
					{
						cerr << "Error: Quantity cannot be less than 0\n"
							<< "Re-enter quantity\n?";
						std::cin >> quantity;
					}
					//updates quantity
					setQuantity(quantity);
					//seek position to input data
					addRecordInfo.seekp((getRecordNumber() - 1) * sizeof(Hardware));
					//write user input into file
					addRecordInfo.write(reinterpret_cast<const char*>(&_records), sizeof(_records));
					break;
				case COST:
					cout << "Enter new cost\n?";
					std::cin >> cost;
					//if entered less than 0
					while (cost < 0)
					{
						cerr << "Error: Cost cannot be less than 0\n"
							<< "Re-enter cost\n?";
						std::cin >> cost;
					}
					//updates cost
					setCost(cost);
					//seek position to input data
					addRecordInfo.seekp((getRecordNumber() - 1) * sizeof(Hardware));
					//write user input into file
					addRecordInfo.write(reinterpret_cast<const char*>(&_records), sizeof(_records));
					break;
				case EXIT:
					break;
			}
		}
	}
}
I got the first part to work. Changed the while loop in a different part of the code. Problem #2 still does not work
Maybe I'm missing something, but what you're trying to do doesn't quite make sense to me. The first part of the code, "//test data automatically entered in file" uses outRecord.write() in order to generate the data file. Also it uses outRecord.seekp() to move to a specific location within the file, before writing the actual record.

Therefore I would expect to see a corresponding updateRecord.read() being used to read a record from the file. Because the file contains binary data, the >> extraction operator won't work as it expects ordinary text rather than binary data. As well as that, updateRecord.seekg() should be used in exactly the same way as when creating the file in the first part, that is, use the record number to move directly to the required location rather than only seeking to the beginning.

Also, I don't understand what is the purpose of having separate ofstream and ifstream passed to the function updateExistingRecord(), unless there are two separate physical files involved.

In order to understand the file layout I created my own test program to try it out. I created a version of the Hardware class with the following data members:
1
2
3
4
5
6
class Hardware {
private:
    int     recordNumber;
    char    toolName[24];
    int     quantity;
    double  cost;


and then created the data file like this (based on your code above).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include "hardware.h" 

#include <iostream>
#include <fstream>

    using namespace std;

void createfile(const string & fname, int n);
void fill(const string & fname);

int main()
{
    cout << "size: " << sizeof(Hardware) << endl;
    string filename = "records.dat";
    
    createfile(filename, 100);
    
    fill(filename);
    
    return 0;
} 
    
void createfile(const string & fname, int n)
{
    ofstream fout(fname.c_str(), ios::binary);  
    
    Hardware record;
    
    for (int i=0; i<n; i++)
    {
        fout.write( reinterpret_cast<const char *>(&record), sizeof(record)  );
        
    }
}

void fill(const string & fname)
{
    // test data automatically entered in file
    // arrays to store test data
    
    const int num = 8;       // number of entries
    Hardware records;        // constructor zeros out each data member
    int    recordArray[num]   = { 3, 17, 24, 39, 56, 68, 77, 83 };
    string toolNameArray[num] = { "Electric sander     ", "Hammer", "Jig saw", "Lawn mower",
                                  "Power saw", "Screwdriver", "Sledge hammer", "Wrench" };
    int    quantityArray[num] = { 7, 76, 21, 3, 18, 106, 11, 34 };
    double costArray[num]     = { 57.98, 11.99, 11.00, 79.50, 99.99, 6.99, 21.50, 7.50 };
    
    fstream outRecord(fname.c_str(), ios::in | ios::out | ios::binary);
    
    // output test data to file
    for (int i = 0; i < num; ++i)
    {
        records.setRecordNumber(recordArray[i]);
        records.setToolName(toolNameArray[i]);
        records.setQuantity(quantityArray[i]);
        records.setCost(costArray[i]);
        // seek position to output data
        outRecord.seekp((records.getRecordNumber() - 1) * sizeof(Hardware));
        // write user input into file
        outRecord.write(reinterpret_cast<const char*>(&records), sizeof(records));
    }    
    
}


In order to test this, I made a second program which reads the entire file in both forward and reverse order, but only outputs the non-zero records.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include "hardware.h" 

#include <iostream>
#include <fstream>

    using namespace std;

void readfile(const string & fname);
void readsome(const string & fname);

bool getRecord(ifstream & is, int n, Hardware & rec);

int main()
{
    string filename = "records.dat";
    
    readfile(filename);
    readsome(filename);

    
    return 0;
} 
    
void readfile(const string & fname)
{
    ifstream fin(fname.c_str(), ios::binary);  
    
    Hardware record;
    int count  = 0;
    int active = 0;
    
    while (fin.read( reinterpret_cast<char *>(&record), sizeof(record)  ) )
    {
        count++;
        
        if (record.getRecordNumber())
        {                 
            cout << record << endl;
            active++;
        }    
    }
    
    cout << "\nTotal records:  " << count  << endl;
    cout << "active records: " << active << "\n\n" << endl;
    
}
    
void readsome(const string & fname)
{
    ifstream fin(fname.c_str(), ios::binary);  
    
    Hardware record;

    for (int i=100; i>0; i--)
        if (getRecord(fin, i, record))
            if (record.getRecordNumber())
                cout << record << endl;
        
}

bool getRecord(ifstream & is, int n, Hardware & rec)
{
    is.seekg((n - 1) * sizeof(Hardware), ios::beg);
    
    is.read( reinterpret_cast<char *>(&rec), sizeof(rec) );
    
    return is;    
}


almost forgot, I used a friend function in the Hardware class, to make it easy to display the record:
1
2
3
4
5
6
7
8
9
10
    friend std::ostream & operator <<(std::ostream & os, const Hardware & rec )
    {
        os << std::fixed << std::setprecision(2);
        os << std::setw(4)                        << rec.recordNumber 
           << "  " << std::left  << std::setw(22) << rec.toolName
           << std::right << std::setw(4)          << rec.quantity 
           << std::setw(8)                        << rec.cost;
           
        return os;
    }


output from the second program:
   3  Electric sander          7   57.98
  17  Hammer                  76   11.99
  24  Jig saw                 21   11.00
  39  Lawn mower               3   79.50
  56  Power saw               18   99.99
  68  Screwdriver            106    6.99
  77  Sledge hammer           11   21.50
  83  Wrench                  34    7.50

Total records:  100
active records: 8


  83  Wrench                  34    7.50
  77  Sledge hammer           11   21.50
  68  Screwdriver            106    6.99
  56  Power saw               18   99.99
  39  Lawn mower               3   79.50
  24  Jig saw                 21   11.00
  17  Hammer                  76   11.99
   3  Electric sander          7   57.98


Maybe some of this will be useful - or again, maybe I just missed the point, which is entirely possible :)
Topic archived. No new replies allowed.