Working with files in Qt - c++

Let's say I write some information in a file, and it writes with n cycles, for example as follows:
a,a,a,a,
b,b,b,b,
c,c,c,c,
a,a,a,a,
b,b,b,b,
c,c,c,c,
.......
a,a,a,a,
b,b,b,b,
c,c,c,c,
Now I want to open the file check the first line, find where it repeats and delete everything after that. For my example case let's say I want to wind where a,a,a,a, meets again, and to delete it, and everything after that, getting the follows instead:
a,a,a,a,
b,b,b,b,
c,c,c,c,
Q: How can I do that?

You can use QTextStream to stream your file (so, do not care about RAM).
Then use readLine() function to read one line at a time to QString, and compare with new line.
Here some code sample:
int lineCounter = 0; //count for line
QFile f(filePath);
if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
QTextStream stream(&f);
QString line;
// read the first line
line = stream.readLine();
lineCounter++;
QString hash = QString(QCryptographicHash::hash(line.toAscii(), QCryptographicHash::Md5).toHex());
do
{
line = stream.readLine();
if (QString(QCryptographicHash::hash(line.toAscii(), QCryptographicHash::Md5).toHex()).compare(hash) == 0)
{
// Save data from 1st line to "lineCounter" to new file, or do your own works;
// and then break;
}
lineCounter++;
} while (!line.isNull());

You want to remove duplicate lines in a file. If you follow the next steps you will get what you want.
Create a vector that will store the hashes of unique lines ( QVector<QString>) Notice that using QMap would be faster.
Create an ouput file
For every line in the file calculate it's hash. Use QCryptographicHash or qHash (in this case you should have a vector of uints.
If the calculated hash is contained in the vector skip this line
Otherwise add the hash to the vector and print the line to the output file.
At the end the output file should contain only unique instances of the input file.

Related

Difference between CSV files which makes different outcome using getline()

I'm writing a function which reads a CSV file using getline() and converts data to the vector of vectors. To test it I've tried to read two files with the same delimiter: one imported from the internet and second exported from R datasets. The first few lines of each looks like:
File1.csv
User ID,Category 1,Category 2,Category 3,Category 4,Category 5,Category 6,Category 7,Category 8,Category 9,Category 10
User 1,0.93,1.8,2.29,0.62,0.8,2.42,3.19,2.79,1.82,2.42
User 2,1.02,2.2,2.66,0.64,1.42,3.18,3.21,2.63,1.86,2.32
User 3,1.22,0.8,0.54,0.53,0.24,1.54,3.18,2.8,1.31,2.5
User 4,0.45,1.8,0.29,0.57,0.46,1.52,3.18,2.96,1.57,2.86
File2.csv
"","Sepal.Length","Sepal.Width","Petal.Length","Petal.Width"
"1",5.1,3.5,1.4,0.2
"2",4.9,3,1.4,0.2
"3",4.7,3.2,1.3,0.2
"4",4.6,3.1,1.5,0.2
However getline() works only for the first one. In second case it simply returns white space. The function performs similar even if I copy single lines from one file to another (of course adding or removing additional colums) -- the rows from file1 will be always properly read while those from file2 never. I've even tried removing " chars, but without much improvement. However switching from comas to '\t' solves the problem.
I'm curious what's the difference between those two files that makes such different outcome?
The source code of my function:
vector<vector<string>> readData(string fileName,int firstLine,char delimeter){
//Open data file
fstream fin;
fin.open(fileName, ios::in);
//Data stored in 2d vector of strings
vector<vector<string>> data;
vector<string> row;
string line,word,temp;
//Read data
int i=0;
while(fin>>temp){
row.clear();
//Read line and store in 'line'
getline(fin,line);
//Don't read first n lines
if (i<firstLine){
i++;
continue;
}
cout<<line<<endl;
//Break words
stringstream s(line);
//Read every column and store in in 'word;
while(getline(s,word,delimeter)){
row.push_back(word);
}
//Append row to the data vector
data.push_back(row);
}
//Close file
fin.close();
return data;
}
The problem is here:
while(fin>>temp){
row.clear();
//Read line and store in 'line'
getline(fin,line);
fin >> temp reads everything till the first space or newline. It is not clear why you do that as only with getline(fin,line) you then try to read the full line and you are not using temp. In the first file fin>>temp consumes only "User", in the second file it consumes the full line, because there are no spaces.
If you look at the read data from the first file you will also notice that the first part of each line is missing.
Tip: Use more meaningful names for your variables. I didn't manage to fully understand your logic, because variables named s and the presence of row and line at the same time causes me headaces.

QT: ASSERT: "uint(i) < uint(size())" in file C:\Qt\Qt5.14.2\5.14.2\mingw73_64\include/QtCore/qstring.h, line 1029

I have a file of 10 rows, I want to do a specific thing that is not important in this context. After reaching the end of a line, the program crash with the error in the title.
I went in the line of the header file it's referring:
{ Q_ASSERT(uint(i) < uint(size())); return QChar(d->data()[i]); }
but still I don't understand. This is my block of code:
QFile file("file.txt");
QTextStream in(&file);
QString s;
int i = 0;
while(!in.readLine().at(i).isNull()) {
s = s + in.readLine().at(i);
++i;
}
EDIT: What I'm trying to do is copy in the QString variable "s" a line of a file character by character.
It means that i is not a valid index for whatever is being returned from readLine(). In other words i is bigger than the length of the line.
The logic in the loop is really curious. I'm sure it's not doing what you think it's doing.
I think what you are trying to write is something like this
QFile file("file.txt");
QTextStream in(&file);
QString s;
QString line = in.readLine();
int i = 0;
while (!line.at(i).isNull()) {
s = s + line.at(i);
++i;
}
That code just reads one line, your code reads multiple lines.
I see two issues (unless I misunderstand how at() and readLine() work):
1) In the while() statement you are reading in a line with ...in.readLine()..., but then in the loop you read in again with another ...in.readLine().... So at the start of the first iteration you read in a line, and then as the body of the loop executes you read in a line again.
2) For every iteration of while you are incrementing i and grabbing that character from in. So for the first iteration (i=0) you grab the 0th character, for the second (i=1) you grab the 1st character.... and so on.

C++ append to second line of file

I would like to append entries in a simple csv file at the second line. The first line contains my column headers and the newest entry has to be on top.
My thought is to read in the first line until the '\n', then move to the next line and write there, but i don't know if that is the most efficient solution. Can someone provide an example?
Since you have stated in the comments that this file will not be very large I would suggest you read in the header into some sort of container. Then insert after the header the newest data that needs to be inserted into the file. Then read in the rest of the file. After you do that then you can write the contents of the container back into the file. Here is a simple demonstration:
std::ifstream fin("somefilename");
std::vector<std::string> file;
file.reserve(30); // grow the capacity to save on allocations
std::string reader;
std::string new_data = "some new data";
getline(fin, reader);
file.push_back(reader); //add header
file.push_back(new_data); // add new line
while(getline(fin, reader)) // get rest of file
file.push_back(reader);
std::ofstream fout("somefilename");
for (const auto & e : file)
fout << e << "\n";

How to DELETE a line(s) in C++?

I am new to file-handling...
I am writing a program that saves data in text-files in the following format:
3740541120991
Syed Waqas Ali
Rawalpindi
Lahore
12-12-2012
23:24
1
1
(Sorry for the bad alignment, it's NOT a part of the program)
Now I'm writing a delete function for the program that would delete a record.
So far this is my code:
void masterSystem::cancelReservation()
{
string line;
string searchfor = "3740541120991";
int i=0;
ifstream myfile("records.txt");
while (getline(myfile, line))
{
cout << line << endl;
if (line==searchfor)
{
// DELETE THIS + THE NEXT 8 LINES
}
}
}
I've done a bit of research and have found out that there is no easy way to access the line of a text file so we have to create another text file.
But the problem arises that how do I COPY the records/data before the record being deleted into the NEW text file?
Open the input file; read one line at a time from the input file. If you decide to want to keep that line, write it to the output file. On the other hand, if you want to 'delete' that line, don't write it to the output file.
You could have a record per line and make even more easy for example:
3740541120991|Syed Waqas Ali|Rawalpindi|Lahore|12-12-2012|23:24|1|1
and the | character saparating each field. This is a well known technic knows as CSV (Comma separated Values)
This way you don't have to worry about reading consecutive lines for erase a record and add a record access the file only once.
So your code becoms into:
void masterSystem::cancelReservation()
{
string line;
string searchfor = "3740541120991";
ifstream myfile("records.txt");
while (getline(myfile, line))
{
// Here each line is a record
// You only hace to decide if you will copy
// this line to the ouput file or not.
}
}
Don't think only about removing a record, there are others operations you will need to do against this file save a new record, read into memory and search.
Think a moment about search, and having your current desing in mind, try to answer this: How many reservations exists for date 12-12-2012 and past 12:00 AM?
In your code you have to access the file 8 times per record even if the other data is irrelevant to the question. But, if you have each record in a line you only have to access file 1 time per record.
With a few reservations the diference is near 0, but it grows exponentially (n^8).

How to avoid reading "\n" when using QFile::readAll function

I have a "sequence.dat" file that contains "1"s and "-1"s in a vertical representation (i.e.: each element is in a single line).. I am trying to read the file as follow:
QFile sequence("Sequences.dat");
sequence.open(QIODevice::ReadOnly);
QByteArray data = sequence.readAll();
for(int i=0; i<29; i++){
signedNo[i] = data[i]; // debugging breaking point
}
sequence.close();
however, at the debugging breaking point, the QByteArray "data" contains "1, -, 1, \n" instead of "1,-1" ...
is there is away to read the whole line at once and not each byte individually ? and ...
if there is not, how to tell the "readAll" function to avoid the "\n" (it is not an optimal solution because I want also to read "-1" and not "- and 1" separately)
QFile::readAll() returns a byte array which contains each and every byte of the file as a separate element.
For your use case, you need to read the file line by line.
The QFile documentation shows some approaches how to do this, for example:
QVector<int> elements;
QFile sequence("Sequences.dat");
if (!sequence.open(QIODevice::ReadOnly | QIODevice::Text))
return;
QTextStream in(&sequence);
while (!in.atEnd()) {
QString line = in.readLine();
elements.append(line.toInt());
}
Despite the fact that this sample is from the Qt documentation, I would recommend to check the return value from in.readLine() which returns a null QString when the end of the file has been reached, instead of using atEnd().
You could read line by line, and you could process it right after you read the line:
i = 0;
while (!sequence.atEnd()) {
QByteArray line = sequence.readLine();
signedNo[i] = line[i];
i++;
}