I read through a file once to find the number of lines it contains then read through it again so I can store some data of each line in an array. Is there a better way to read through the file twice than closing and opening it again? Here is what I got but am afraid it's inefficient.
int numOfMappings = 0;
ifstream settingsFile("settings.txt");
string setting;
while(getline(settingsFile, setting))
{
numOfMappings++;
}
char* mapping = new char[numOfMappings];
settingsFile.close();
cout << "numOfMappings: " << numOfMappings << endl;
settingsFile.open("settings.txt");
while(getline(settingsFile, setting))
{
cout << "line: " << setting << endl;
}
settingsFile.clear();
settingsFile.seekg(0, settingsFile.beg);
To rewind the file back to its beginning (e.g. to read it again) you can use ifstream::seekg() to change the position of the cursor and ifstream::clear() to reset all internal error flags (otherwise it will appear you are still at the end of the file).
Secondly, you might want to consider reading the file only once and storing what you need to know in a temporary std::deque or std::list while you parse the file. You can then construct an array (or std::vector) from the temporary container, if you would need that specific container later.
It's inefficient, use a std::vector and read through the file once only.
vector<string> settings;
ifstream settingsFile("settings.txt");
string setting;
while (getline(settingsFile, setting))
{
settings.push_back(setting);
}
Just use:
settingsFile.seekg(0, settingsFile.beg);
This will rewind file pointer to the very beginning, so you can read it again without closing and reopening.
Related
I am trying to add elements into a .json file between [] as last.
How can I move the cursor to add elements between [...] with efficiently with std::ofstream?
I have tried several open modes but there are strange things. First I created this question about not able to use the file streaming for read and write because of the overwrite issue.
#include <iostream>
#include <fstream>
int main ()
{
char errmsg[2048];
std::ofstream ostream;
ostream.exceptions(std::ios_base::badbit);
try
{
ostream.open("LS22731.json", std::fstream::ate | std::fstream::in);
strerror_s(errmsg, 2048, errno);
std::cout << "Error (" << errno << "): " << errmsg << std::endl;
if (ostream && ostream.is_open())
{
auto ppos = ostream.tellp();
std::streampos sub = 1; //
std::cout << "Tellp: " << ppos << std::endl; // Always show zero but file has large data
if (ppos > 1)
ostream.seekp(ppos - sub) << "aa";
ppos = ostream.teelp();
std::cout << "New tellp: " << ppos << std::endl;
ostream.close();
}
}
catch (std::ios_base::failure& fb)
{
std::cout << "Failure: " << fb.what() << std::endl;
char errmsg[2048];
strerror_s(errmsg, 2048, errno);
std::cout << "Error (" << errno << "): " << errno << std::endl;
}
}
I searched about open modes then I found this but is it good to open file with both mode std::fstream::ate | std::fstream::in together for std::ofstream? And when I open the file with std::fstream::out mode it is rewriting so deleting whole document,
std::fstream::out: Delete all contents of the file (overwrite)
std::fstream::app: Cannot move the cursor with seekp
std::fstream::ate: Delete all contents of the file (overwrite)
std::fstream::binary: Delete all contents of the file (overwrite)
std::fstream::ate | std::fstream::app: Cannot move the cursor with seekp
std::fstream::ate | std::fstream::out: Delete all contents of the file (overwrite)
std::fstream::ate | std::fstream::in: Can move the cursor but not insert delete all after.
I don't want to use c FILE.
Well JSON files are err... sequential text files. That means that the file contains a stream of bytes representing the JSON content. And AFAIK, no filesystem has provision for inserting data in the middle of a sequential file. The foolproof way is:
copy up to the insertion point to a temp file
write the new data
add the remaining data from the original file
rename the old file to a backup name
rename the temp file with the original name
(optionaly) remove the backup file
The brave way is to move the second part up by chunks starting from the end to create an emply place to put the data write the new data in that place, and pray all along the operation for no problem in the middle because the file would be irremediably corrupted.
Those 2 ways can process files of arbitrary sizes. For small files, you could load everything in memory, write the new data at the insertion point and rewrite the remaining data after the new data. You just need to use a default fstream and use neither ate nor trunc. out does not mean deleting all the file content. You simply replace the original bytes at the place where you write.
So you should use:
ostream.open("LS22731.json", std::fstream::out | std::fstream::in);
Then you:
read up to your insertion point and discard the data
note the position with tellp
read the end of file and save it
go to the insertion point
write the new data
write the saved data
close the stream
Here is an adaptation of the previous algorithm. The cautious points as:
you must use a fstream with std::fstream::out | std::fstream::in mode to be able to read and write a file. The file must exist and you will be initially positioned at the beginning of the file
to reliably be able to compute positions, you must open the file in binary mode (std::fstream::binary)(should be possible in text mode but I could not find my way...)
Here is a close adaptation of your code: it opens the file, search for the first closing bracket (]), and inserts ,"h" before to simulate adding a value into a list.
...
std::fstream ostream;
ostream.exceptions(std::ios_base::badbit);
try
{
// use binary mode to ba able to relyably seek the file.
ostream.open("LS22731.json",
std::fstream::out | std::fstream::in | std::fstream::binary);
strerror_s(errmsg, 2048, errno);
std::cout << "Error (" << errno << "): " << errmsg << std::endl;
if (ostream && ostream.is_open())
{
std::streampos ppos;
// search the first ]
ostream.ignore(std::numeric_limits<std::streamsize>::max(), ']');
// we want to insert just before it
ppos = ostream.tellg() - std::streampos(1);
ostream.seekg(ppos); // prepare to read from the ]
std::string old = "", tmp;
// save end of file, starting at the ]
while (std::getline(ostream, tmp)) {
old += tmp + "\n";
}
ostream.clear(); // clear eof indicator
ostream.seekp(ppos, std::ios::beg); // go back to the insertion point
ostream << ",\"h\""; // add some data
ostream << old; // add the remaining of the original data
ostream.close();
}
...
Disclaimers:
DO NOT PRETEND I ADSISED YOU THIS WAY. If there is a problem in the middle of processing, the file will be irremediately corrupted.
it will fail miserabily if a text field contains a closing bracket, because it is not a JSON parser
If you open a file for reading, you cant set the write head of it.
You are using std::ofstream with ios::in mode which I'm not sure is effective. but std::ofstream must be opened with ios::out or ios::app. When you override the default you should give also the default.
If you need to open a file for both read and write, you should use std::fstream.
Another issue is that you trying to add some string in the middle of a text file, and it is not so good idea, it is not similar to paste some string in a text file when opened in Notepad. you must replace a section with another section with the same length, pushing some string won't move the rest of the data forward.
I think the easy way is to read the whole JSON to memory, process it by add or remove some data, and finally rewrite the whole JSON to the file.
I'm trying to use same fstream object for first write the file and after that read the file.
when I'm using below code then the codes of writing the file is working but I'm getting junk output instead of texts which written in the file.
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
int main() {
fstream file;
file.open("test.txt",ios::in|ios::out| ios::trunc);
if (!file) {
cout << "Error";
}
else {
cout << "success";
file <<"\n\n1st Line\n 2nd line \n 3rd line\n";
string filecontent;
while (file.good()) {
getline(file, filecontent);
cout << filecontent << endl;
}
}
return 0;
}
Output
This code has two separate problems. The first (which others have already pointed out to at least some degree) is that your loop isn't detecting the end of the file correctly. In fact, almost any time you use while (!file.eof()) or while (file.good()), it's going to be a mistake--it won't detect end of file at the right time, so (for example) when you reach the end of the file, you won't detect it at the right time, and you'll see the last item in the file appear to be read twice before the loop exits.
In addition to that, however, you have a problem in that you're writing to the file, then immediately trying to read. That's simply not allowed--you want to do a seek any time you switch between reading and writing.
In this case, you have a bit of a further problem. Since you've just written data into the file, your file's current position is at the end of the file. So even if you could just start reading without seeking, you'd start reading from the end of the file. That, of course, would immediately fail.
So you also really need to seek back to the beginning of the file to be able to read it back in.
So, the big changes here are adding a line like: file.seekg(0); after you finish writing, but before you start to try to read that data back in, and then changing your reading loop to something like:
while (getline(file, filecontent)) {
cout << filecontent << endl;
}
One last point: although it's not going to make a big difference in this case, I'd advise using "\n" instead of std::endl. std::endl writes a new-line and flushes the file buffer. When you're writing to the screen it won't make any real difference, but when writing to a normal file flushing the buffer unnecessarily can and will slow your code substantially (10x slower is pretty common).
I'm fairly new to C++ so please forgive me if my terminology or methodology isn't correct.
I'm trying to write a simple program that:
Opens two input files ("infileicd" and "infilesel").
Opens a single output file "list.txt".
Compares "infilesel" to "infileicd" line by line.
If a line from "infilesel" is found in "infileicd", it writes that line from "infileicd" to "list.txt", effectively making a separate log file.
I am using the getline() function to do this but have run into trouble when trying to compare each file line. I think it might be easier if I could use only the substring of interest to use as a comparison.
The problem is that there are multiple words within the entire getline string and I am only really interested in the second one. Here are two examples:
"1529 nic1_mau_op_mode_3 "8664afm007-01" "1" OUTPUT 1 0 LOGICAL 4 4136"
"1523 pilot_mfd_only_sel "8664afm003-02" "1" OUTPUT 1 0 LOGICAL 4 4112"
"nic1_mau_op_mode_3" and "pilot_mfd_only_sel" are the only substrings of interest.
It would make it a lot easier if I could only use that second substring to compare but I don't know how to extract it specifically from the getline() function. I haven't found anything suggesting it is impossible to do this, but if it is impossible, what would be an alternative method for extracting that substring?
This is a personal project so I'm under no time contstraints.
Any assistance is greatly apprecated in advance. Here is my code (so far):
int main()
{
//Open the file to write the selected variables to.
ofstream writer("list.txt");
//Open the selected variabels file to be read.
ifstream infilesel;
infilesel.open("varsel.txt");
//Open the icd file to be read.
ifstream infileicd;
infileicd.open("aic_fdk_host.txt");
//Check icd file for errors.
if (infileicd.fail()){
cerr << "Error opening icd.\n" << endl;
return 1;
}
else {
cout << "The icd file has been opened.\n";
}
//Check selected variables file for errors.
if (infilesel.fail()){
cerr << "Error opening selection file.\n" << endl;
return 1;
}
else {
cout << "The selection file has been opened.\n";
}
//Read each infile and copy contents of icd file to the list file.
string namesel;
string nameicd;
while(!infileicd.eof()){
getline(infileicd, nameicd);
getline(infilesel, namesel);
if (nameicd != namesel){ //This is where I would like to extract and compare the two specific strings
infileicd; //Skip to next line if not the same
} else {
writer << nameicd << namesel << endl;
}
}
writer.close();
infilesel.close();
infileicd.close();
return 0;
}
So, based on what we discussed in the comments, you just need to toss the stuff you don't want. So try this:
string namesel;
string nameicd;
string junk;
while(!infileicd.eof()){
// Get the first section, which we'll ignore
getline(infileicd, junk, ' ');
getline(infilesel, junk, ' ');
// Get the real data
getline(infileicd, nameicd, ' ');
getline(infilesel, namesel, ' ');
// Get the rest of the line, which we'll ignore
getline(infileicd, junk);
getline(infilesel, junk);
Basically, getline takes a delimiter, which by default is a newline. By setting it as a space the first time, you get rid of the first junk section, using the same method, you get the part you want, and then the final portion goes to the end of the line, also ignoring it.
I want to read and write compressed file with my C++ script. For this purpose, I use the gzstream lib. It works fine with a very simple example like this :
string inFile="/path/inputfile.gz";
igzstream inputfile;
ogzstream outputfile("/path/outputfile.gz");
inputfile.open(inFile.c_str());
// Writing from input file to output file
string line;
while(getline(inputfile, line)) {
outputfile << line << endl;
}
But in my C++ script, things are more complicated and my output files are created within a dynamic vector.
For UNcompressed files, this way worked very fine :
string inFile="/path/uncompressedInputFile.ext";
ifstream inputfile;
vector <ofstream *> outfiles(1);
string outputfile="/path/uncompressedOutputFile.ext";
outfiles[1] = new ofstream(outputfile.c_str());
inputfile.open(inFile.c_str());
string line;
while(getline(inputfile, line)) {
*outfiles[1] << line << endl;
}
Now with compressed file, this way produces me corrupted files :
string inFile="/path/compressedFile.gz";
igzstream inputfile;
vector <ogzstream *> outfiles(1);
string outputfile="/path/compressedOutputFile.gz";
outfiles[1] = new ogzstream(outputfile.c_str());
inputfile.open(inFile.c_str());
string line;
while(getline(inputfile, line)) {
*outfiles[1] << line << endl;
}
I got a "compressedOutputFile.gz" in my path, not empty, but when trying to uncompressed it I got "unexpected end of file" which, I guess, means the file is corrupted....
What's wrong with it ? Can anyone please help me ?! :)
In the simple example, the GZip file is closed automatically when the ofstream is destroyed, which flushes its remaining buffer to disk.
In the dynamic example, you're not closing because the object is being created on the heap. In both cases, this could result in the loss of data at the end of the file, depending on the format. Since GZip is compressed, it's more likely to lose more relevant data, resulting in a more obvious failure.
The best solution is to create a vector<unique_ptr<ogzstream> >, which cause it to automatically destroy streams when they go out of scope. The less optimal solution is to remember to manually delete each pointer prior to exiting the function.
Edit: And as a quick note, as pointed out by #doctorlove in the original comments, you need to use the correct index, otherwise you're causing other issues.
How do i find a string in a file? In my code, i want to find the name of a person in the file. and do the actions in the comments. Here is my code:
int main(){
size_t found;
ofstream myfile;
cout << "Enter the name you wish to delete." << endl;
getline(cin, name);
myfile.open("database.dat");
found=myfile.find(name);
if (found!=string::npos){
number = myfile.tellg();
/*Delete current line and next line*/
}
}
Do you want to modify the file, or simply skip those two lines while reading?
Actually, the solution is the same for both, because removing data from the middle of the file requires reading everything after that and rewriting it with an offset (just like removing an element from the middle of an array).
So, read the entire file into memory except for any lines you determine need to be deleted (just skip those). After that, write the surviving lines back to disk. It's probably a good idea to use a temporary file which gets moved to the original name as a final step, so that data isn't destroyed if your process is aborted.