How can I check if a file contains only integers? - c++

In my project I need to upload a file and check that inside it there is an integer (positive or negative) for each line so that it can be represented within a vector.
Since I am working on QT and C++ I have considered two proposals, but both when I insert a number in the file for example "35.2" with a comma and I press the start button the program crashes.
Is there a better solution to mitigate the problem? I am attaching one of the solution that I was evaluating.
QFile file_in(file_name);
file_in.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream in(&file_in);
int elem;
int pos = 0;
QString s;
QIntValidator valid(-999, 999, this);
while(!in.atEnd())
{
in >> elem;
s = QString::number(elem);
if(!valid.validate(s,pos))
{
v.clear();
QMessageBox msg(this);
msg.setText("Input non valido");
msg.exec();
return;
}
else
{
v.push_back(elem);
}
}

A simpler solution would be to read the file line by line and check if the current line is convertible to an integer.
Qt already provides a lot of convenience methods. For instance, you would be interested by QByteArray::toInt().
Hence a solution could be:
bool validate(const QString & file_name)
{
QFile in_f(file_name);
if(!in_f.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
bool is_valid = true;
while(is_valid && !in_f.atEnd())
{
QByteArray line = in_f.readLine();
line.toInt(&is_valid); // is_valid is set to false if the conversion failed
}
return is_valid;
}
This way there is no crash. If each line contains an integer, it will return true, otherwise, if at least one line contains something else (string, double, ...), it will return false.
As implemented here, if the file is empty, the function will return true. Of course you can add a check for this case if you want to return false instead.

Please read the doc about validate method here:
https://doc.qt.io/archives/qt-4.8/qintvalidator.html#validate
you are using a criteria for valid inputs as valid(-999, 999, this);
so if the list in the file is holding a row with 1000, using a toInt method will return you a false positive result!
the result is not just True or False like you may think...
you actually get:

Related

How to preserve zeros at end of doubles in QJsonDocument?

I'm reading, parsing and writing back a JSON file in a QT project. Part of the requirements is that all entry's should be the same written out as declared in the source file.
One of the entries looks somewhat like this:
"SomeVal": 1.23141241242140
When I read the JSON object, the last zero (0) is removed. That does make sense, since it is not needed for normal use cases. But, since I need to preserve the whole correct number, including that last zero, this is incorrect for me. Any clue on how I can bypass this problem?
The code that reads the JSON file:
QString rawJson = "";
QFile jsonFile(filePath);
if(jsonFile.exists())
{
jsonFile.open(QFile::ReadOnly|QFile::Text);
rawJson = jsonFile.readAll();
jsonFile.close();
}
else
{
return false;
}
QJsonParseError json_parse_error;
QJsonDocument json_doc = QJsonDocument::fromJson(rawJson.toUtf8(), &json_parse_error);
if(json_parse_error.error != QJsonParseError::NoError)
{
emit SignalMessageCritical(QString(json_parse_error.errorString()));
return false;
}
this->jsonTestFile = json_doc.object();

bool function doesn't work as a while condition

I am trying to to check if a file has successfully opened, read from it and output what I've read from it all in one function, because I have 7 files to operate on in the same code and I want to avoid writing the same code over and over again.
So I have made a bool function and put it as a while condition.
If I succeed, the function returns true and if I don't it returns false. So a while(!function) should keep trying until it works, correct ? And the answer is yes, it works as intended.
But if I change the condition of the while to while(function) one would expect to repeat the function until it fails somehow (maybe it can't open the file.). But it doesn't behave as expected. It only works correctly on the first while iteration.
This is the example:
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <fstream>
#include <sstream>
bool readConfig(std::fstream& file, std::string (&str)[10], std::string identity) {
if(file.is_open()) {
if(file.seekg(0)) {
std::cout<<"Start from 0"<<std::endl;
}
// Get content line by line of txt file
int i = 0;
while(getline(file, str[i++]));
std::cout<<"i= "<<i<<std::endl;
for(int k = 0; k<i; k++) {
std::cout<<identity<<" = "<<str[k]<<std::endl;
}
return true;
} else {
std::cout<<"ERROR ! Could not open file."<<std::endl;
return false;
}
}
int main() {
char configFilePath[]="test.txt";
std::fstream configFile;
configFile.open(configFilePath, std::fstream::in);
std::string test[10];
std::string id = "testing";
while(!readConfig(configFile, test,id)) {
usleep(1000*1000);
};
return 0;
}
This is the content of test.txt :
line 1
line 2
line 3
line 4
This is the output:
Start from 0
i= 5
testing = line 1
testing = line 2
testing = line 3
testing = line 4
testing =
i= 1
testing = line 1
i= 1
testing = line 1
and so on.
Why does it work on the first iteration but then it stops at i=1 ? I am asking because I don't know if what I did is correct or not. while(!function) works, but maybe it won't work all the time, maybe my code is flawed.
Or maybe while(getline(configFile, string[i++])); is at fault here ?
This is the code I am trying to replace:
void readConfig(std::fstream& configFile, std::string (&str)[10], std::string identity) {
if(configFile) {
// Get content line by line of txt file
int i = 0;
while(getline(configFile, str[i++]));
//for debug only
if((i-1) == 0) {
std::cout<<identity<<" = "<<str[i-1]<<std::endl;
} else {
for(int k = 0; k<i-1; k++) {
std::cout<<identity<<" = "<<str[k]<<std::endl;
}
}
} else {
log("ERROR ! Could not get content from file.");
}
}
int main() {
file.open(file, std::fstream::in);
if(file.is_open()) {
std::cout<<"Successfully opened URL Display Text file."<<std::endl;
std::string inputs[10];
std::string id = "url_text";
readConfig(file, inputs, id);
file.close();
} else {
// Could not open file
log("Error ! Could not open file.");
}
}
I do this 7 times, instead of just calling a function 7 times, that does all of that.
But if I change the condition of the while to while(function) one would expect to repeat the function until it fails somehow (maybe it can't open the file.).
You reasoning is off here. The function is not opening the file, so that is nothing that can go wrong on the next iteration when it suceeded on the first.
What the function does is: it reads all the contets of the file, then returns true. And subsequent iterations there is nothing left to read, but still the function returns true.
You should check if the file is open only once, not in each iteration. If the function is supposed to read a single line then make it so, currently it reads all.
Change the test from if (file.is_open()) to if (file). Failing to open the file is not the only way that a file stream can end up in a bad state. In particular, on the second call to this function, the stream is open, but it's in a failed state because the last read attempt failed.
If you just want to read the file line by line, print the lines and store them, I'd do it like this.
Rather than using a c-style array use a std::vector or std::array
Check the if the file is open before you call the read function
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
void readConfig(std::ifstream& configFile,
std::vector<std::string>& lines,
const unsigned int limit) {
std::string line;
while (std::getline(configFile, line)) {
if (lines.size() >= limit) {
break;
}
lines.push_back(line);
}
}
int main() {
const std::array<std::string, 3> fileNames = {"test1.txt",
"test2.txt",
"test3.txt"};
// Iterate over all your files
for (const auto& fileName : fileNames) {
// Open the file
std::ifstream configFile(fileName);
if (!configFile.is_open()) {
std::cout << "ERROR! Could not open file.\n";
continue;
}
// Read the file
std::vector<std::string> lines;
constexpr unsigned int limit = 4;
readConfig(configFile, lines, limit);
if (configFile.is_open()) {
configFile.close();
}
// Work with the file content
std::cout << fileName << "\n";
for (const auto& line : lines) {
std::cout << "testing = " << line << "\n";
}
std::cout << "\n";
}
return 0;
}
Your output paints a fairly clear picture of what is going on. You have enough debugging output to identify what choices have been made. They key point I would focus on is the following sequence:
testing =
i= 1
The first of these lines is the fifth line read from your four-line file. Not surprisingly, there is nothing there. The next output line comes from the next invocation of readConfig, somewhere in the branch where file.isopen() is true. However, note that there is not a line saying "Start from 0" between these two. That means file converted to false after the call to file.seekg(0) (the value returned by that function is file, not directly a boolean). This indicates some sort of error state, and one should expect that error state to persist until cleared. And there is no attempt made to clear it.
The next bit of code is the getline loop. As with seekg, the getLine function returns the stream (file) rather than a boolean. As expected, the error state has persisted, making the loop condition false, hence no iterations of the loop.
testing = line 1
The next line of output is ambiguous. It could indicate that the position was successfully changed to the start of the file, and that the first line of input was successfully read. Or it could indicate that the call to getLine returned before erasing the provided string, leaving the contents from the first call to readConfig. I'm thinking the latter, but you could check for yourself by manually erasing str[0] before the getline loop.
In general, reusing resources like this makes debugging harder because the results could be misleading. Debugging would be less confusing if str was a local variable instead of a parameter. Similar for file – instead of a stream parameter, you could pass a string with the name of the file to open.

QTextStream Comparing first character of each line in a csv file with a known variable?

Title pretty much says it all, but I better be a little more specific so as not to confuse any one(including myself).
Been at this for two days not much success.
I have a CSV file and open/read it line by line with QTextStream. The line length is set to first character of each line only, line.at(0). It compares the beginning of each line with a know variable/integer.
It works and the label does show "Match Found" but immediately afterwards, the app crashes. It never reaches the"No Matches Found".
The error I'm getting:
ASSERT: "uint(i) < uint(size())" in file ..\..\..\..\Qt\5.0.2\mingw47_32\include/QtCore/qstring.h, line 729
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
Any ideas? Anyone?
Oh yeah, here is the code to the function causing this headache.
void DialogToll::ReadAndCompare()
{
QString Number = ui->Tolls->text();
QFile filetoCompare("C:/Tolls.txt");
if(filetoCompare.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream stream(&filetoCompare);
QString line;
do
{
line = stream.readLine();
if(line.at(0)== Number)
{
ui->label->setText("match Found!!!");
}
}while(!line.isNull());
ui->label->setText("No match Found!");
}
}
You read file line by line, but it looks like some of the lines have zero length, so that your line.at(0) fails. I would improve your code in the following way:
[..]
QTextStream stream(&filetoCompare);
do
{
QString line = stream.readLine();
if (line.size() > 0 && line.at(0) == Number)
{
ui->label->setText("match Found!!!");
}
} while (!line.isNull());
ui->label->setText("No match Found!");
[..]
Note that I check for the line's length before comparing its first character.
Like #Vahancho said: you're not checking your line before accessing it's elements. This will cause a crash for empty lines.
Did you realize that you overwrite the ui label after the loop
finished? You can prevent that by returning early:
Next, you may want to use the power of QRegexp for this:
void checkMatch() {
....
QRegexp expr("^"+ui->Tolls.text());
do {
auto line = stream.readLine();
if (expr.exactMatch(line)) {
ui->label->setText("Match");
return; // <<<< early return
}
} while(!stream.atEnd());
ui->label->setText("no match");
}

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

File I/O logic with a while statement, how would this code be expected to behave?

I'm trying to understand some differences in file i/o techniques. Suppose I have the following code:
FILE *work_fp;
char record[500] = {0};
while(!feof(work_fp))
{
static int first = 1;
fgets(record, 200, work_fp);
if (first)
{
var1 = 2;
length += var1;
}
first = 0;
if (feof(work_fp))
{
continue;
}
if((int)strlen(record) < length)
{
fclose(work_fp);
std::ostringstream err;
err << "ERROR -> Found a record with fewer bytes than required in file."
<< std::endl;
throw std::runtime_error(err.str());
}
const int var2 = 1;
if(memcmp(argv[1], record + var2, 3) == 0)
{
load_count_struct(record, var1);
}
}
I'm not seeing how the second if argument can be true.
if (feof(work_fp))
{
continue;
}
If feof(work_fp) is true wouldn't the while argument be false? Then the continue could never get called?
FOLLOW UP QUESTION:
Ok, I see how fgets can cause work_fp to reach eof conditions.
Suppose I want to try and implement this another way. Using getline(), for example.
std::string data(file);
std::ifstream in(data.c_str());
if (!in.is_open())
{
std::ostringstream err;
err << "Cannot open file: " << file << std::endl;
throw std::runtime_error(err.str());
}
std::string buffer = "";
std::string record = "";
while (getline(in, buffer))
{
static int first = 1;
if (first)
{
var1 = 2;
length += var1;
}
first = 0;
if (//What should go here?!?)
{
break;
}
// etc...
}
Any suggestions? I'm thinking
if (buffer == std::string::npos)
no?
The line:
fgets(record, 200, work_fp);
can advance to read/write head to the end of the file, thus changing the return value on feof.
First of all, your code invokes undefined behaviour, because you've not initialized work_fp, yet you're using it, passing it to feof(), first in while(!feof(work_fp))
, and elsewhere in the code.
Anyway, supposing you initialize it by opening some file, then I would answer your question as follows:
The following code reads some data from the file using work_fp, that means, it is possible that feof(work_fp) will return true in the second if condition, because after reading data using fgets(), the file pointer work_fp may reach end of file.
fgets(record, 200, work_fp);
In the while loop fgets() is called and the file pointer is advanced. Then if(feof(work_fp)) checks if the end of the file is reached. If so then continue the while loop. The while loop then continues if the end of the file is NOT reached, which in this case will be false. Hence the logic works.
That is a weird statement, and I think it should be
if (feof(work_fp)){
break;
}
The continue; can get called, since it occurs after an fgets, but calling continue is pointless since that brings execution to the next iteration of the loop which is guaranteed to be false and quit the loop. It makes more sense, and is more readable/understable to put break; there.
Since you have a fgets within the while before your check on feof, the feof status of work_fp may have changed during that read, in which case, it may evaluate to true.
There is a read operation on work_fp between the while and if conditions, so that feof() could be true.
The eof can have been reached at the following line:
fgets(record, 200, work_fp);
So right after having been evaluated to false in the while statement.
This would make the
if (feof(work_fp))
evaluated to true.
But this code can be simplified.