How to efficiently read the last line of file in c++ [duplicate] - c++

This question already has answers here:
c++ fastest way to read only last line of text file?
(7 answers)
Closed 5 years ago.
I need to read the last line of file that is growing while some calculations are made. At the beginning there is no problem because the file is small, but when the file gets bigger the reading process is to slow.
while(!file.eof()){
if ((std::getline(file,line))){
std::istringstream iss(line);
std::istream_iterator<double> it(iss), end;
std::vector<double> v(it, end);
std::copy(v.begin(), v.end(), arr);
v.clear();
line.clear();
}
}
What is a more efficient way to read the last line of a file with out reading all the file until the end is reach?

use seekg() to jump to the end of file, and read backward - something like this
if(fs.is_open())
{
fs.seekg(-1, std::ios_base::end);
if(fs.peek() == '\n')
{
fs.seekg(-1, std::ios_base::cur);
int i = fs.tellg();
for(i;i > 0; i--)
{
if(fs.peek() == '\n')
{
//Found
fs.get();
break;
}
fs.seekg(i, std::ios_base::beg);
}
}
std::string lastline;
getline(fs, lastline);

Use this algorithm:
Seek until the end of the file.
Copy in reverse until newline is reached (skip the newline if file ends in it). EDIT: This step is actually a bit tricky with streams. Memory mapping would make this easier, however keep in mind that there is no standard API for memory mapping.
The copy is now the last line in reverse.
You could just reverse search for the newline and copy in forward direction, or just reverse the inverted line, or you could copy into the front of a double ended queue so that the line is reversed while copying, but perhaps fastest might be to keep it reversed and process it with reverse iterators, depending on what you intend to do with the line.

Related

How to read just before EOF from a file and put it into a string? [duplicate]

This question already has answers here:
How do I read an entire file into a std::string in C++?
(23 answers)
Closed 3 years ago.
My function reads a file and puts it into a string in order for me to process it. I need to read just before EOF, obviously. The problem is that the EOF character is also put inside the string and I can't find a way to bypass it, since it leds other parts of the program to fail. I link the function below.
string name_to_open, ret = string();
ifstream in;
getline(cin, name_to_open);
in.open(name_to_open.c_str());
if (!in.is_open()) {
cout << "Error." << endl;
return string();
}
else {
ret += in.get();
while (in.good()) {
ret += in.get();
};
};
in.close();
return ret;
The function reads fine until the end of the file, then appends EOF and \0. How can I solve the problem? Does the EOF character work fine in controls? I also tried to put a line ret[ret.size() - 1] = '\0'; at the end of the cycle, but this doesn't seem to work either.
ret += in.get(); appends the character read from the tile to the string whether the value read was good or not. You need to 1) read, 2) test that the read is valid and the value read is safe to use, 3) use the value read. Currently your code reads, uses, and then tests whether or not the value read was safe to use.
Possible solution:
int temp;
while ((temp = in.get()) != EOF) // read and test. Enter if not EOF
{
ret += static_cast<char>(temp); // add the character
};
Note: get returns an int, not a char. This is to be able to insert out-of-band codes such as EOF without colliding with an existing valid character. Immediately treating the return value as a char could result in bugs because a special code may be mishandled.
Note: there are many better ways to read an entire file into a string: How do I read an entire file into a std::string in C++?

Reading file into two arrays

I'm trying to write my own vocabulary with a test for my little brother, but I have a problem when I want to read data from file into two arrays - first with English words, and second with Polish words. File looks alike
black - czarny
red - czerwony etc.
And my function:
void VOC::readout()
{
fstream file;
VOC *arr = new VOC;
string line;
file.open("slowka.txt");
if(file.good())
{
int i=0;
while(!file.eof())
{
getline(file, line);
size_t pos = line.find(" - ");
int position = static_cast<int>(pos);
file>>arr[i].en;
file>>arr[i].pl;
++i;
}
}
}
I thought it could be a good idea to insert a line into first array until the function finds " - ", and after that insert the rest of line into second array, but I have some problems with that. Could someone help me? I know I can solve it by using std::vector but I care to do that by using arrays.
If you insist on using plain arrays, you'll first have to count the number of lines in your file and then allocate enough memory. Arrays -- unlike std::vector objects -- won't grow automatically but have a fixed size.
That being said, note that using !file.eof() is not the best way to read a stream until the end is reached. You can use the simpler
std::string line;
while (std::getline(file, line)) {
// ...
}
idiom instead, which also takes care of error conditions. See this question (and corresponding answers) for more information on that.

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;
}

Problems with reading text from a file [duplicate]

This question already has answers here:
Read whole ASCII file into C++ std::string [duplicate]
(9 answers)
Closed 9 years ago.
I have this function that reads the text from a file and adds it to a string, now the weird thing is that it works fine if its a short text. But if its a longer text the string becomes empty, any help solving this problem is appreciated.
string inlasning(string namn)
{
string filString, temp;
ifstream filen(namn.c_str());
if(!filen.good())
{
cout << "Otillganglig fil" << endl;
filString = "ERROR";
return filString;
}
else
{
while(!filen.eof())
getline(filen, temp);
filString.append(temp);
}
filen.close();
return filString;
}
1) Don't use eof() to control the loop. Put getline directly into the loop condition. Search StackOverflow if you have problems doing this.
2) Your while loop has no braces and thus only covers the getline line, despite your misleading indentation.
3) getline discards newlines. Your final string will be wrong.
4) The actual behavior you're observing comes from the fact that you only append the very last thing that getline returns to your string. When your file contains one line of text and doesn't end in a newline, this will seem to work. If it has more lines but doesn't end in a newline, you'll only get the last line. If the file does end in a newline, because of your incorrect loop condition the last call to getline will actually give you an empty string, which will be exactly the contents of your string.
Replace
while(!filen.eof())
getline(filen, temp);
filString.append(temp);
with
while(!filen.eof())
{
getline(filen, temp);
filString.append(temp);
}
Use "is_open()" to check if the file exists:
if( ! filen.is_open() ){...} // you don't need an else clause
...And your while loop must has braces or it will only execute the getline(...) instruction:
while( filen.good() ) {
getline( filen , temp );
filString += ( temp + '\n' );
}
If your file doesn't ends with '\n', remove the last char from the string

reading from file to vector- last line gets repeated [duplicate]

This question already has answers here:
Reading from text file until EOF repeats last line [duplicate]
(7 answers)
Testing stream.good() or !stream.eof() reads last line twice [duplicate]
(3 answers)
reading a line in text file twice
(4 answers)
Closed 9 years ago.
I am trying to read values from a file to a vector
std::vector<float> setTimesArray (std::string flName){
int i=0, dummy=0;
float temp;
std::vector<float> pObs;
std::string line;
std::ifstream inFile;
inFile.open(flName.c_str());
if(!inFile){
std::cout<<"\n.obs file not valid. Quitting programme...";
exit(1);
}
while(inFile.good()){
i++;
getline(inFile, line);
if(i>=3){ //I don't want first two lines
std::istringstream in(line);
in>>dummy;//discards first value in the line
in>>temp;
pObs.push_back(temp);
in.str(""); //discards remaining part of the line
}
}
return pObs;
inFile.close();
}
Problem is, the last value gets repeated. For example, flName had total 975 lines. Thus pObs must be having size=973 (975-2 initial lines). But the size is 974 and I see that the last value is repeating. What mistake have I made?
try:
while (getline(inFile,line))
instead of while(inFile.good())
and remove the getline() call from within the method.
You may also want to change your last two lines of codes to this, as per Daniel Kamil Kozar's suggestion:
inFile.close();
return pObs;
After the last line, good() is still allowed to return true. It doesn't have to return false until after a failed read. Thus, if it returns true, and then fails the read, your line variable won't take a new value. The correct solution would probably be to correct the bounds checking, but in this case, moving the declaration of line into the scope of you while loop and checking for and empty string should correct the issue.