Go to a specific line in file and read it - c++

Problem description
I have a file containing a set of lines. A
File 1:
"Hello How are you"
"The cat ate the mouse"
Based on the the beginning and ending of the lines given by the user as input. I want to go to each line in the file and Extract it.
For examples if user types 1 , 17 then I have to go to line 1 that has a size of 17 characters. He can give any line number in the file.
I read the following Answer Read from a specific spot in a file C++. But I didn't really understand. Why do the lines have to be the same size? If i have the information concerning the beginning and ending of every line in the file. Why can't I access it directly?
Source code
I tried to use the following code which was inspired by Read Data From Specified Position in File Using Seekg But I couldn't extract the lines why?
#include <fstream>
#include <iostream>
using namespace std::
void getline(int, int, const ifstream & );
int main()
{
//open file1 containing the sentences
ifstream file1("file1.txt");
int beg = 1;
int end = 17;
getline(beg,end, file1);
beg = 2;
end = 20;
getline(beg,end, file1);
return 0;
}
void getline(int beg, int end, const ifstream & file)
{
file.seekg(beg, ios::beg);
int length = end;
char * buffer = new char [length];
file.read (buffer,length);
buffer [length - 1] = '\0';
cout.write (buffer,length);
delete[] buffer;
}

This code appears to be using line numbers as byte offsets. If you seek to offset "1" the file seeks forward 1 byte, not 1 line. If you seek to offset 2, the file seeks forward 2 bytes, not 2 lines.
To seek to a specific line you need to read the file and count the number of line breaks until you get to the line you want. There is code that already does this, for example std::getline(). If you don't already know the exact byte offset of the line you want, you can call std::getline() the number of times equal to the line number you want.
Also remember that the first byte of a file is at offset 0 not offset 1, and that different platforms use different bytes to indicate the end of a line (E.g. on Windows it's "\r\n", on Unix it's "\n"). If you're using a library function to read lines, the line ending should be taken care of for you.

Related

Reading numbers from a chosen line in a file C++

I want to know if I can read numbers from a chosen line in a file in c++.For example if I have .txt file like :
2 3
1 2 3 4
4 5 6 7
There are 3 lines, how can I read only the numbers on line 2 without having to read anything else?
Unless you know the exact file offset of the second line from a previous call to std::istream::tellg, then you will have to read the first line in order to get to the position of the second line. You can use the function std::getline for reading the first line as a std::string, or you can use std::istream::ignore to read and discard the first line, like this:
input.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
If you do happen to know the exact offset of the second line, then you can call std::istream::seekg in order to directly jump to that offset.
Note, howevever, that a file offset does not necessarily correspond to the number of characters that you see when reading the file in text mode. For example, on different platforms, line endings may consist of a different number of characters, which get translated to the single character \n when reading the file in text mode. However, the file offset required by std::istream::seekg is the offset in binary mode. Therefore, you should generally not attempt to calculate such an offset yourself (unless you opened the stream in binary mode, which you should not do for text files). You should only use the function std::istream::tellg for obtaining such an offset.
You can read the file line by line in C++ using
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(){
fstream newfile;
newfile.open("file.txt",ios::in);
if (newfile.is_open()){
string tp;
int i=0;
while(getline(newfile, tp)){
if (i==1) {
cout << tp << endl; // this will only print the second line
}
i+=1;
}
newfile.close();
}
}

deleting a line in a .txt file without using another file or array in C++

the professor gave told us to delete a line in a txt file without the help of another file or an array,
i tried to replace the line with backspace but it print the BS character instead
void rem()
{
fstream f("test.txt");
f.seekp(3, ios_base::beg);
f.write("\b",sizeof(char));
f.close();
}
1
2
3
4
5
i want to remove 2
1
3
4
5
after searching for few hours i found that everyone use another file or a vector or the try to replay the line with BS like me.
EDIT:
this is the correct code:
void skip_line(std::fstream& f)
{
char c;
f.get(c);
f.ignore(50, '\n');
}
void getpos(int& readpos, int& writepos)
{
std::fstream f("test.txt", std::ios::in | std::ios::binary);
skip_line(f);
writepos = f.tellg();
skip_line(f);
readpos = f.tellg();
f.close();
}
void rem()
{
int writepos, readpos, length, newSize;
std::fstream f;
getpos(readpos, writepos);
f.open("test.txt");
length = readpos - writepos;
f.seekg(readpos);
for (char c; f.get(c);)
{
f.seekg(writepos);
if (c != '\n') f.put(c);
readpos++;
writepos++;
f.seekg(readpos);
}
f.close();
//fs::path p = "test.txt"
newSize = fs::file_size("test.txt") - length;
fs::resize_file("test.txt", newSize);
}
the rusult:
befor
111111
222222
333333
444444
555555
after
111111
333333
444444
555555
Backspace will not do what you hoped for. A backspace character takes up one char just like any other character. When printed on devices capable of moving the cursor backwards, that's what'll happen. It's just a visual thing and it does not work with files.
Since you are not allowed to use another file or arrays, I'm going to assume that std::vectors and std::strings are also forbidden so I suggest shifting everything down in the file, one char at a time, to overwrite the line to be removed.
You will need a function like std::getline which is capable of reading a line from a stream into a std::string - but you do not need to store any data so we can call it skip_line. It could look like this:
std::istream& skip_line(std::istream& is) {
// read until reading fails or a newline is read:
for(char ch; is.get(ch) && ch != '\n';);
return is;
}
When you've opened the file, call skip_line until you've reached the line you want to remove. If you want to remove line 2, call skip_line 1 time. If you instead want to remove line 3, call skip_line 2 times.
The get (f.tellg()) position in the stream is now where you should start writing when you move everyting in the file back to overwrite the line to be removed. Store this position in a variable called writepos.
Call skip_line one time. The get position is now where you should start reading when moving the contents of the file. Store this position in a variable called readpos.
Calculate and store the length of the line to be removed: lenght_of_line_to_be_removed = readpos - writepos.
Now, you need to read one char at a time from the readpos position and write that char to the writepos position. It could look like this:
f.seekg(readpos); // set the _get_ position where we should read from
for(char ch; f.get(ch);) { // loop for as long as you can read a char
f.seekp(writepos); // set the _put_ position where you should write to
f.put(ch); // ...and write the char
writepos += 1; // step both positions forward
readpos += 1; // -"-
f.seekg(readpos); // set the new _get_ position
}
When the above is done, everything is "shifted down" in the file - but the size of the file will still be the same as it was before:
original: 1 2 3 4 5
after : 1 3 4 5 5
If you use C++17 or newer, you can use the standard functions std::filesystem::file_size and std::filesystem::resize_file to fix this. Remember that you stored lenght_of_line_to_be_removed above. If you use a version of C++ that does not have std::filesystem, you need to use some platform specific function. Posix systems have the truncate function that can be used for this.

how to reverse order of lines in a file [duplicate]

This question already has answers here:
How can I reverse the order of lines in a file?
(24 answers)
Closed 8 years ago.
How can we revere order of lines in a file not the lines themselves.
File can get huge.
No assumption should be made about the length of a line.
Input:
this is line1
this is line2
this is line3
Example Output:
this is line3
this is line2
this is line1
I though of making use of another file as buffer, like a stack data structures, but could not really go anywhere with it.
Any thoughts on this ?
Read in large blocks of the file starting at both ends. Inside those blocks, swap the first line for the last line and then move both pointers to keep track of where you are. Write out each block as you fill it. When the two pointers meet in the middle, you are done.
Don't try to modify the blocks in place, that will make things more complicated. Use four blocks, the first read block, the first write block, the last read block, and the last write block. As each write block is complete, write it out. As each read block is exhausted, read in another one. Be careful not to overwrite anything you've not yet read!
It should be fairly straightforward, just tedious. If you don't need it to be optimal, you can just read blocks backwards and write out a new file and then move it on top of the existing file.
If the file won't fit in memory, then it's a two-pass process. The first pass, you read chunks of the file (as many lines as will fit into memory), and then write them to a temporary file in reverse order. So you have:
while not end of input
read chunk of file into array of lines
write lines from array to temporary file, in reverse order
end while
When you're done with the first pass, you'll have a bunch of temporary files: temp1.txt, temp2.txt, temp3.txt ... tempN.txt.
Now open the last file (tempN.txt) for append, and start appending the files in reverse order. So you have:
open fileN for append
fileno = N-1
while fileno > 0
append file_fileno to fileN
fileno--
end while
Then rename tempN.txt and delete the other temporary files.
By the way, you can use the operating system supplied concatenation utility for step 2. On Windows, for example, you could replace step 2 with:
copy /A file4.txt+file3.txt+file2.txt+file1.txt mynewfile.txt
There are similiar utilities on other platforms.
You might run into command line length limitations, though.
it can be done in 2 simple steps:
step 1: reverse all the file
step 2: reverse each line
step:0 1 2
---------------------
abc zyx xyz
1234 => 4321 => 1234
xyz cba abc
EDIT: here is a complete solution:
#include <iostream>
#include <fstream>
#include <algorithm>
#define BUFFSIZE 4098 /*make sure this is larger then the longest line...*/
using namespace std;
bool reverse_file(const char* input, const char* output)
{
streamsize count=0;
streamoff size=0,pos;
char buff[BUFFSIZE];
ifstream fin(input);
ofstream fout(output);
if(fin.fail() || fout.fail()){
return false;
}
fin.seekg(0, ios::end);
size = fin.tellg();
fin.seekg(0);
while(!fin.eof()){
fin.read(buff, BUFFSIZE);
count = fin.gcount();
reverse(buff,buff+count);
pos = fin.tellg();
if(pos<0) {
pos = size;
}
fout.seekp(size - pos);
fout.write(buff,count);
}
return true;
}
bool reverse_file_lines(const char* input, const char* output)
{
streamsize count=0;
char buff[BUFFSIZE];
ifstream fin(input);
ofstream fout(output);
if(fin.fail() || fout.fail()){
return false;
}
while(!fin.eof()){
fin.getline(buff, BUFFSIZE);
/*if BUFFSIZE is smallest then line size gcount will return 0,
but I didn't handle it...*/
count = fin.gcount();
if(buff[count-1]==0)count--;
reverse(buff,buff+count);
fout.write(buff,count);
if(!fin.eof()){
fout<<endl;
}
}
return true;
}
int main()
{
reverse_file("test.in", "test.tmp");
reverse_file_lines("test.tmp","test.out");
return 0;
}

Avoid \r\n while reading text from a binary file

I have a binary file packing lots of files (something like a .tar), where I can found both binary and text files.
When processing in memory strings, carriage lines are usually '\n', but if I read the text part from this packed file, I get "\r\n". Therefore processing this text gives me errors.
Here is the code for reading the text from a binary file:
FILE* _fileDescriptor; // it's always open to improve performance
fopen_s(&_fileDescriptor, _filePath.string().c_str(), "rb");
char* data = new char[size + 1]; // size is a known and correct value
fseek(_fileDescriptor, begin, SEEK_SET); // begin is another known value, where the file starts inside the packed one
fread(data, sizeof(char), size, _fileDescriptor);
data[it->second.size] = '\0';
This gives me the right text into data, but the following code gives me error when reading an empty line:
istringstream ss(data); // create a stringstream to process it in another function
delete[] data; // free the data buffer
// start processing the file
string line;
getline(infile, line); // read an empty line
if(line.size() > 0) {
/*
enters here, because the "empty" line was "\r\n", and now the value of line is '\r', therefore line.size() == 1
*/
...
So, any advice to avoid the '\r'?
I edited it on Notepad++. Changing its configuration to use '\n' instead of '\r\n' as line carriage works, but I don't want to depend on this because other people can miss that, and it would be very hard to spot the problem if that happens.
Probably easiest to trim the '\r' characters out of your string and then discard blank lines. See this answer for approaches to trimming a std::string (I'm assuming that's what 'line' is):
What's the best way to trim std::string?

How to edit a text file with C++

How do I edit a specific line of a text file in C++? Let's say I want to open a file and change the focus or pointer or whatever its called to line 17 column 20. That way I can edit text after line 17, column 20.
I tried this, but it didnt work.
ofstream txtFile("textFile.txt");
fseek(txtFile, 17, 20);
txtFile << "New stuff to enter at this point (overwrites old not insert)";
How do I do this?
fseek is not seeking counting lines, but rather bytes. What you instruct the program is not to position the pointer at column 20 of 17th line, but rather at the 17 + 20 = 37th byte of the file.
The first parameter of the function is the origin, i.e. the count of bytes from the origin from which you count, and the second - how many more you offset.
See the reference of fseek.
I am not aware of any library that can do byte positioning in respect of lines and columns in C++. You will probably need to use a higher level function and parse lines one by one (e.g. using getline if you are after C++ solution).
Use fputs, like it's done here. In order to recognize new lines, you have to loop through the content and count the newline characters (i.e.'\n' on linux). You can get the character currently pointed to with getc.
One way to implement this is to write a function that returns the byte-position of where a given row starts, like so:
#include <stdio.h>
int getRowPos(int row,FILE* fp){
int pos=1,lines=1,currChr;
do{
currChr=getc(fp);
if(currChr=='\n')
lines++;
}while(lines<row && currChr!=EOF && ++pos);
return pos;
}
Using fseek one could then go to the position returned by getRowPos plus the column number (i.e. byte number, on the line), and then write the desired content using fputs, like so:
int row=wanted row
int col=wanted column
FILE * pFile;
//Open file for read and write
pFile = fopen ( "myfile.txt" , "rb+" );
int rowPos=getRowPos(row,pFile);
fseek ( pFile , rowPos+colPos , SEEK_SET );
fputs ( "my new content" , pFile );
fclose ( pFile );
If you know how wide your lines are, say n wide, you can add an lseek skipping n bytes ahead before the call to getc.