Seekg in C++
Introduction to Seekg in C++
The C++ function ‘seekg’ falls under input stream manipulation. Input streams, like file streams, are typically used to set the position of the input stream to a certain position within the stream. When working with files, this Function from the Standard Template Library (STL) is frequently used to browse content rapidly.
Whether you're working with file streams or standard input/output streams, you can find the ‘seekg’ function in the header files ‘iostream>’ and ‘fstream>’.
Syntax of Seekg:
Here's the basic syntax of the seekg Function:
istream& seekg (streampos position);
istream& seekg (stream off offset, ios_base::seekdir way);
- Position: The file pointer should be set to this absolute location inside the stream.
- Offset: This option designates a point inside the stream that is relative.
- Way: It specifies the direction in which you wish to relocate the file pointer about your present location or the start of the stream. One of the following values may apply to it:
- ios_base::beg (beginning of the stream)
- ios_base::cur (current position in the stream)
- ios_base::end (end of the stream)
A reference to the input stream (istream&) on which it was called is returned by the seekg Function. You can chain together different stream operations with this.
Common Use Cases of Seekg
In C++, the seeking Function is frequently used for a variety of tasks, including
- Reading Particular Sections of a File: You should avoid reading the full File while working with huge files. Instead, you can read data from a specified location by moving the file pointer using ‘seekg’.
- Random Access: To move to a specific record or data point in a file without reading everything that comes before it, ‘seekg’ is necessary for files that support random access (like binary files).
- Parsing Structured Data: If a file, such as a database or configuration file, contains structured data, you can use ‘seekg’ to go to particular sections or records.
- Error Handling: ‘seekg’ helps handle reading-related errors in files. Use seekg to move the file pointer to a known good spot and go on reading from there if you run into problems when reading a file.
Example 1: Use of seekg in Basic:
Let's start with a straightforward example to learn how to use seekg to read data from a file at a given location. Create a text file with some content for this example, and then use seekg to read from various locations inside the File.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// Create and open a text file for reading
ifstream inputFile("sample.txt");
// Check if the File is opened successfully
if (!inputFile) {
cerr << "Failed to open the file." << endl;
return 1;
}
// Set the file pointer to the beginning of the File (position 0)
inputFile.seekg(0);
// Read and print the first 100 characters
char buffer[101]; // 100 characters + null terminator
inputFile.read(buffer, 100);
buffer[100] = '\0'; // Null-terminate the string
cout << "First 100 characters: " << buffer << endl;
// Set the file pointer to position 200
inputFile.seekg(200);
// Read and print the next 50 characters
inputFile.read(buffer, 50);
buffer[50] = '\0'; // Null-terminate the string
cout << "Next 50 characters: " << buffer << endl;
// Close the File
inputFile.close();
return 0;
}
In this instance:
- Using ifstream, we open the "sample.txt" text file for reading.
- We verify that the File was successfully opened. If not, we quit after printing an error notice.
- To set the file pointer to the File's beginning, we use ‘seekg(0)’.
- We publish the first 100 characters that we read from the File.
- The file pointer is set to position 200 using ‘seekg(200)’.
- The next 50 characters are read from the File and printed.
This example shows how seekg enables selective data reading by allowing us to shift the file pointer to particular locations inside the File.
Example 2: Binary File Seek:
Binary File, as an example with binary files, where you might need to hop to specified points to retrieve structured data, seek ‘seekg’ is also frequently used. Here is an illustration involving a binary file that contains student records.
#include <iostream>
#include <fstream>
using namespace std;
// Define a structure for student records
struct StudentRecord {
int rollNumber;
char name[50];
double marks;
};
int main() {
// Create and open a binary file for reading
ifstream binaryFile("students.dat", ios::binary);
// Check if the File is opened successfully
if (!binaryFile) {
cerr << "Failed to open the file." << endl;
return 1;
}
// Function to read and display a student record
auto displayStudent = [](const StudentRecord& student) {
cout << "Roll Number: " << student.rollNumber << endl;
cout << "Name: " << student.name << endl;
cout << "Marks: " << student.marks << endl;
cout << "-------------------" << endl;
};
// Set the file pointer to the beginning of the File
binaryFile.seekg(0);
// Read and display the first student record
StudentRecord student1;
binaryFile.read(reinterpret_cast<char*>(&student1), sizeof(StudentRecord));
displayStudent(student1);
// Set the file pointer to the third student record
binaryFile.seekg(2 * sizeof(StudentRecord));
// Read and display the third student record
StudentRecord student3;
binaryFile.read(reinterpret_cast<char*>(&student3), sizeof(StudentRecord));
displayStudent(student3);
// Close the File
binaryFile.close();
return 0;
}
In this instance:
- To represent student records, which comprise a roll number, name, and grades, we construct a ‘StudentRecord’ structure.
- We use ifstream to open the "students.dat" binary File for reading and specify that it is a binary file by using the ‘ios::binary’ flag.
- As in the previous example, we determine whether the File was successfully opened.
- To display the contents of a ‘StudentRecord’, we define the lambda function displayStudent.
- To read the first student record, we use ‘seekg(0)’ to set the file pointer to the File's beginning.
- To read the third student record, we set the file pointer to that place using ‘seekg(2 * sizeof(StudentRecord))’.
- We close the binary File at the end.
This example shows how to efficiently read structured data using seekg to browse specified locations within a binary file.
‘tellg’ Function:
It's crucial to comprehend the ‘tellg’ function before delving further into ‘seekg’. The file pointer's current location within an input stream can be found with the command ‘tellg’. This is helpful when you wish to save the position before using ‘seekg’ and later return to that location.
Syntax of tellg:
The ‘tellg’ syntax is as follows:
streampos tellg();
- The ‘streams’ object that ‘telling’ returns as the current position of the file pointer denotes the location inside the stream.
Let's revise the preceding illustration to show how to record the File's current location and then go back to it using the ‘tellg’ command.
#include <iostream>
#include <fstream>
using namespace std;
// Define a structure for student records
struct StudentRecord {
int rollNumber;
char name[50];
double marks;
};
int main() {
// Create and open a binary file for reading
ifstream binaryFile("students.dat", ios::binary);
// Check if the File is opened successfully
if (!binaryFile) {
cerr << "Failed to open the file." << endl;
return 1;
}
// Function to read and display a student record
auto displayStudent = [](const StudentRecord& student) {
cout << "Roll Number: " << student.rollNumber << endl;
cout << "Name: " << student.name << endl;
cout << "Marks: " << student.marks << endl;
cout << "-------------------" << endl;
};
// Get and store the current position
streampos initialPosition = binaryFile.tellg();
// Set the file pointer to the beginning of the File
binaryFile.seekg(0);
// Read and display the first student record
StudentRecord student1;
binaryFile.read(reinterpret_cast<char*>(&student1), sizeof(StudentRecord));
displayStudent(student1);
// Return to the initial position
binaryFile.seekg(initialPosition);
// Read and display the second student record
StudentRecord student2;
binaryFile.read(reinterpret_cast<char*>(&student2), sizeof(StudentRecord));
displayStudent(student2);
// Close the File
binaryFile.close();
return 0;
}
In this example:
We use the' tell' command to obtain the file pointer's current position and store it in the ‘initial position’ variable.
We utilize ‘seeking’ to return to the ‘initial position’ after reading the first student record and then reading the second student record.
We can efficiently change the file pointer and then return to the original location while maintaining the integrity of the file reading process by combining ‘tellg’ and ‘seekg’.
‘seekg’ with Relative Positions
We have been using ‘seekg’ with absolute file positions up to this point. Moving the file pointer relative to its present location or the File's end is frequently more practical, though. This is especially helpful if you need to skip a specific number of bytes or seek to the File's end but need to know the absolute position.
Let's make an example where we use ‘seekg’ and relative placement to show this.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// Create and open a binary file for reading
ifstream binaryFile("data.bin", ios::binary);
// Check if the File is opened successfully
if (!binaryFile) {
cerr << "Failed to open the file." << endl;
return 1;
}
// Read and display the first integer from the File
int firstInt;
binaryFile.read(reinterpret_cast<char*>(&firstInt), sizeof(int));
cout << "First integer in file: " << firstInt << endl;
// Use seekg to move the file pointer 4 bytes ahead (to the second integer)
binaryFile.seekg(4, ios::cur);
// Read and display the second integer
int secondInt;
binaryFile.read(reinterpret_cast<char*>(&secondInt), sizeof(int));
cout << "Second integer in file: " << secondInt << endl;
// Use seekg to move the file pointer 8 bytes before the end of the file
binaryFile.seekg(-8, ios::end);
// Read and display the last double in the File
double lastDouble;
binaryFile.read(reinterpret_cast<char*>(&lastDouble), sizeof(double));
cout << "Last double in file: " << lastDouble << endl;
// Close the File
binaryFile.close();
return 0;
}
In this instance:
- For reading, we open a binary file called "data.bin".
- The File's first integer is read and displayed.
- We skip the second integer by moving the file pointer 4 bytes forward using 'seekg(4, ios::cur)’.
- The second integer is read and shown.
- To change the file pointer 8 bytes before the File's end, we use ‘seekg(-8, ios::end)’.
- The last double in the File is read and shown.
This example shows how to traverse through a file using ‘seekg’ using relative positions based on the current position or relative to the File's end.
Seekable File Types
The kind of File determines whether you may search within it. Text and binary files are the two broad categories into which seekable file types can be classified.
1. Text files:
Text files may usually be searched for. A text file pointer can be moved around using the ‘seekg’ command. However, when working with multibyte character encodings, the fact that seeking in a text file is based on characters rather than bytes might need to be revised.
2. Binary Files:
When exact byte-level control is required, binary files are frequently the best option because they are also seekable. Within a binary file, ‘seekg’ can be used to relocate the file pointer to a certain number of bytes.
Combining ‘seekg’ and ‘tellg’
When reading data from a file, you occasionally need to maintain track of where you are in the File. In these situations, the use of ‘seekg’ and ‘tellg’ together can be very effective.
Let's write a sample program that reads a file with a straightforward record structure and prints the records together with their locations in the File.
#include <iostream>
#include <fstream>
using namespace std;
// Define a structure for a record
struct Record {
int id;
char name[50];
};
int main() {
// Create and open a binary file for reading
ifstream binaryFile("records.bin", ios::binary);
// Check if the File is opened successfully
if (!binaryFile) {
cerr << "Failed to open the file." << endl;
return 1;
}
//Function to read and display a record
auto displayRecord = [](const Record& record, streampos position) {
cout << "Position: " << position << endl;
cout << "ID: " << record.id << endl;
cout << "Name: " << record.name << endl;
cout << "-------------------" << endl;
};
// Read and display records along with their positions
streampos currentPosition;
while (true) {
currentPosition = binaryFile.tellg(); // Get the current position
Record record;
binaryFile.read(reinterpret_cast<char*>(&record), sizeof(Record));
// Check for end of File
if (binaryFile.eof()) {
break;
}
displayRecord(record, currentPosition);
}
// Close the File
binaryFile.close();
return 0;
}
In this example:
- We read from a binary file called "records.bin"
- We define a Record structure to represent a record in the File consisting of an ID and a name.
- To display a record with its location in the File, we define the lambda function ‘displayRecord’.
- Before reading each record inside the loop, we use ‘tellg’ to get our current location.
- Using ‘eof()’, we read a record and verify that the File is at its conclusion.
- We end the loop if the File's end is reached.
- If not, the record and its location in the File are shown.
This example shows how to read records from a file while keeping track of their locations using ‘seekg’ in conjunction with ‘tellg’.
Seekable and Non-seekable Streams
Some input streams do not allow for searching operations. Depending on the type of stream and its underlying source, you may or may not be able to utilize ‘seekg’ on a certain stream. The following are some general principles:
- File Streams (ifstream): ‘ifstream’ allows the creation of seekable file streams. The file pointer can be set using ‘seekg’ to various locations inside the File.
- Standard Input (cin): It is impossible to seek in the standard input stream (‘cin’). The file pointer cannot be moved within standard Input using ‘seekg’.
- Standard Output Stream (cout): ‘cout’ is not a seekable standard output stream. The file pointer cannot be moved within standard output using ‘seekg’.
- String Streams (istringstream): String streams built with ‘istringstream’ are not seekable (‘istringstream’). The file pointer cannot be moved within a string stream using ‘seekg’.
- Custom Streams: Depending on how it is implemented, a custom stream (such as a network stream or a custom data source) may or may not be seekable. To determine if the custom stream class supports searching, consult its documentation or source code.
It's a good idea to use ‘seekg’ in conjunction with ‘tellg’ to look for problems or the ‘tellg’ return value before using ‘seekg’ to ensure that the stream you're working with supports seeking. If seeking is not supported, using ‘seekg’ could produce unexpected results.
Typical Mistakes & Pitfalls
There are certain frequent mistakes and problems to be mindful of while using ‘seekg’ in C++:
- File Existence: Before opening a file, always ensure it already exists. Runtime errors may occur if this needs to be done.
- File Open Mode: Make sure to open the File in the appropriate mode (for example, binary mode for binary files). A file opened in the incorrect mode may behave unexpectedly.
- Bounds Checking: Exercise caution when using ‘seekg’ to relocate the file pointer. Avoid seeking outside the File's boundaries to avoid errors or behavior that isn't defined.
- Error handling: After using ‘seekg’, always look for errors. To ascertain whether the seek operation was successful, use ‘fail()’ or look at the ‘tellg’ return value.
- Byte vs Character Offset: In text files, ‘seekg’ works with characters rather than bytes. This can produce unexpected search results if you're using multibyte character encodings.
- Non-Seekable Streams: Not all input streams can be searched. Errors may occur if ‘seekg’ is used on streams that can't be searched.
- Concurrency: When using ‘seekg’ and reading from a file, be aware of synchronization to prevent race situations if many threads or processes access the same File.
Conclusion
When working with files, the ‘seekg’ function in C++ effectively modifies the file pointer within input streams. You can navigate to particular locations inside a file, skip data, and read data when you want to. You can maintain track of where you are in the File and go back to prior positions by combining the commands seek and tell.
When using ‘seekg’, remember to handle errors, check for seek ability, and utilize the proper file modes. You can read and process files in your C++ programs more quickly and precisely if you know how to use ‘seekg’ properly.