PDA

View Full Version : Using an array and QString contains



TomJoad
11th April 2011, 15:53
Hello everyone. I'm writing a program that will search for particular words and count if they appear or not and then output to a csv file. I suspect that my problem lies in line 71 when it checks to see if otext contains dataArray[iii][0] and writes "yes" if it does, because the debugresults displays "no" for dataArray[iii][1].

My code:

Easier to read code/comments::




class cdata
{//creation of the cdata class. This is created to protect dataArray from external influences, but can still be accessed through select functions.
public:
void popcountries();
QString debugresults();
void parseInput(QString);

private:
QString dataArray[192][192];

};


void MainWindow::on_parse_B_clicked()
{//button clicked and program starts

QString tmp; //creates the QString for results to be shown
tmp = start(); //start()'s results are the results to be shown
ui.debugResultsTxt->setPlainText(tmp); //results shown
}

QString start()
{
//calls cdata class from above
cdata d;

d.popcountries(); // populates first column of the array from the given file

QString input;
MainWindow mw; //calls mainwindow class
input = mw.oText(); //returns the text to be searched through as the user's input

d.parseInput(input); //uses the user's input to see if it contains dataArray[iii][0] (the first column of the array)

QString tmp; //creates a qstring to return the final/debug results
tmp = d.debugresults(); //debug results. This is used to format the final results as they would appear in the csv file.

return tmp; //returns the formatted final/debug results to on_parse_B_clicked()

}

void cdata::popcountries()
{ //parses the file line by line and populates the first column of dataArray. If the end of the file is reached, it populates the rest with "-1" as a placeholder
QFile file;
QString filename = "countriesList.txt";
file.setFileName(filename);
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream txtstream(&file);
for (int iii=0; iii<192; iii++)
{


QString tmp = txtstream.readLine();
dataArray[iii][0] = tmp;

int xxx = iii+1;
if (txtstream.atEnd() == TRUE)
{
for (xxx ; xxx<192; xxx++)
{
QString tmp2 = "-1";
dataArray[xxx][0] = tmp2;
}
break;
}

}
file.close();

}


QString MainWindow::oText()
{ //grabs the user's input and returns it to start()

QString otext;
otext = ui.tmpTextinput->toPlainText();
return otext;
}

void cdata::parseInput(QString otext)
{//checks to see if the word in dataArray[iii][0] (the first column as populated above) is in otext (the user's input) and writes "yes" in the next column if it is, and "no" if it is not.

for (int iii = 0; iii<192; iii++)
{

int xxx = iii+1;

if (otext.contains(dataArray[iii][0], Qt::CaseInsensitive)) //Check to see if otext has the word from the first column of dataArray --- I believe that this is the problem
{
dataArray[iii][1] = "yes"; //if it does, it writes yes in the next column
}
else
{
dataArray[iii][1] = "no"; //if it doesn't, it writes no in the next column
}
if (dataArray[xxx][0] == "-1") // checks the next row in the first column to see if it is the placeholder "-1". If it is, it breaks the loop.
break;
}



}


QString cdata::debugresults()
{ //formats the array into a string that will eventually become the csv file. For now, it formats them and returns the formatted string to start(), which returns that to on_parse_B_clicked() and displays it.
QString tmp1 = "Test results:,\n";
for (int iii=0; iii<192; iii++)
{

QString tmp2 = dataArray[iii][0];
QString tmp3 = dataArray[iii][1];
tmp2.append(" , " + tmp3 + " , ");
tmp1.append(tmp2 + " \n");

int xxx = iii+1;

if (dataArray[xxx][0] == "-1")
break;

}
return tmp1;
}



Original code in post::

class cdata
{
public:
void popcountries();
QString debugresults();
void parseInput(QString);

private:
QString dataArray[192][192];

};

void cdata::popcountries()
{
QFile file;
QString filename = "countriesList.txt";
file.setFileName(filename);
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream txtstream(&file);
for (int iii=0; iii<192; iii++)
{


QString tmp = txtstream.readLine();
dataArray[iii][0] = tmp;

int xxx = iii+1;
if (txtstream.atEnd() == TRUE)
{
for (xxx ; xxx<192; xxx++)
{
QString tmp2 = "-1";
dataArray[xxx][0] = tmp2;
}
break;
}

}
file.close();

}

QString cdata::debugresults()
{
QString tmp1 = "Test results:,\n";
for (int iii=0; iii<192; iii++)
{

QString tmp2 = dataArray[iii][0];
QString tmp3 = dataArray[iii][1];
tmp2.append(" , " + tmp3 + " , ");
tmp1.append(tmp2 + " \n");

int xxx = iii+1;

if (dataArray[xxx][0] == "-1")
break;

}
return tmp1;
}

void cdata::parseInput(QString otext)
{//checks to see if the country dataArray[iii][0] is listed in otext

for (int iii = 0; iii<192; iii++)
{

int xxx = iii+1;

if (otext.contains(dataArray[iii][0], Qt::CaseInsensitive)) //I believe that this is the problem
{
dataArray[iii][1] = "yes";
}
else
{
dataArray[iii][1] = "no";
}
if (dataArray[xxx][0] == "-1")
break;
}



}

QString MainWindow::oText()
{

QString otext;
otext = ui.tmpTextinput->toPlainText();
return otext;
}

QString start()
{
//calls cdata class
cdata d;

d.popcountries(); //in cdata.cpp and populates first column of the array

QString input;
MainWindow mw; //calls mainwindow class
input = mw.oText(); //in cdata.cpp and returns the users input to search for

d.parseInput(input); //parses the input;compares against the first column of dataAray[iii][0]; writes "yes" or "no" to column two; and in parseInput.cpp

QString tmp;
tmp = d.debugresults(); //debug results. Used for displaying the final results.

return tmp;

}

void MainWindow::on_parse_B_clicked()
{//button clicked and program starts

QString tmp;
tmp = start();
ui.debugResultsTxt->setPlainText(tmp);
}



What could I be doing wrong? I wrote a similar program that uses only a 1d array and it works fine.

high_flyer
11th April 2011, 16:29
What could I be doing wrong?
All kinds of things:
You define a two dimentional array where the second dimension is 192:

QString dataArray[192][192];
But nowhere in your code you use more than '0' or '1' dimensions.
So probably

QString dataArray[192][2];
Is better.

But since you are using C++, and Qt why not use containers?

QVector<QVector<QString>> dataArray;
You win many advantages over you C style array.

Moreover, when you fill your array you use ONLY the '0' dimension, so why do you use a multi dimensional array at all?
In addition, allthough you are filling only the '0' dimension, in debugresults() you read and append from both?!

Then the following code:


for (int iii=0; iii<192; iii++)
{


QString tmp = txtstream.readLine();
dataArray[iii][0] = tmp;

int xxx = iii+1;
if (txtstream.atEnd() == TRUE)
{
for (xxx ; xxx<192; xxx++)
{
QString tmp2 = "-1";
dataArray[xxx][0] = tmp2;
}
break;
}

}


can be translated to:


QVector<QString> oneDimension;
while(!txtstream.atEnd())
{
oneDimension.push_back(txtstream.readLine());
}

oneDimension.push_back(QString::number(-1));
dataArray.push_back(oneDimension);




You problem lies in a messy and unstructured code, which is hard to follow and understand, which probably is the cause to any logical error you are making.
Clean up and structure you code first, add comments, so that YOU too can know what the code is actually trying to do.
Then we can have a second look.

Please don't take my critic personally, all I said is with full respect.
I just called 'em the way I see 'em, and the advice is real and true.

TomJoad
11th April 2011, 17:26
This is what I envision:

The program will open multiple webpages and write "yes" or "no" to the dataArray if the webpage contains the word in dataArray[iii][0]. The reason for [192][192] is because there will be 192 words it checks against, and the proceeding columns will contain "yes" or "no" for each website it checks against. So 0,0 will be a word and 0,1; 0,2; etc. will be the result "yes" or "no." The final output will be to a csv file that will go:

word, yes, no, yes, yes, etc.,
word 2, no, no, yes, no, etc.,
etc.

Right now, I have a txt file it grabs the list of words from to populate the first column (working), and check against an input (what the user inputs) and write the results (not working). The program will then show it in the results window, just for testing purposes.

I appreciate the suggestion of using a vector instead, and it appears to be the better solution instead of using an array, but it didn't address the underlying problem of why the program isn't correctly recognizing that the user's input is a word in the first column of the array and writes "yes" in the next row.

I've gone through and done a better job at commenting on the code. I believe lack of comments from my first post may have created some confusion.


class cdata
{//creation of the cdata class. This is created to protect dataArray from external influences, but can still be accessed through select functions.
public:
void popcountries();
QString debugresults();
void parseInput(QString);

private:
QString dataArray[192][192];

};


void MainWindow::on_parse_B_clicked()
{//button clicked and program starts

QString tmp; //creates the QString for results to be shown
tmp = start(); //start()'s results are the results to be shown
ui.debugResultsTxt->setPlainText(tmp); //results shown
}

QString start()
{
//calls cdata class from above
cdata d;

d.popcountries(); // populates first column of the array from the given file

QString input;
MainWindow mw; //calls mainwindow class
input = mw.oText(); //returns the text to be searched through as the user's input

d.parseInput(input); //uses the user's input to see if it contains dataArray[iii][0] (the first column of the array)

QString tmp; //creates a qstring to return the final/debug results
tmp = d.debugresults(); //debug results. This is used to format the final results as they would appear in the csv file.

return tmp; //returns the formatted final/debug results to on_parse_B_clicked()

}

void cdata::popcountries()
{ //parses the file line by line and populates the first column of dataArray. If the end of the file is reached, it populates the rest with "-1" as a placeholder
QFile file;
QString filename = "countriesList.txt";
file.setFileName(filename);
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream txtstream(&file);
for (int iii=0; iii<192; iii++)
{


QString tmp = txtstream.readLine();
dataArray[iii][0] = tmp;

int xxx = iii+1;
if (txtstream.atEnd() == TRUE)
{
for (xxx ; xxx<192; xxx++)
{
QString tmp2 = "-1";
dataArray[xxx][0] = tmp2;
}
break;
}

}
file.close();

}


QString MainWindow::oText()
{ //grabs the user's input and returns it to start()

QString otext;
otext = ui.tmpTextinput->toPlainText();
return otext;
}

void cdata::parseInput(QString otext)
{//checks to see if the word in dataArray[iii][0] (the first column as populated above) is in otext (the user's input) and writes "yes" in the next column if it is, and "no" if it is not.

for (int iii = 0; iii<192; iii++)
{

int xxx = iii+1;

if (otext.contains(dataArray[iii][0], Qt::CaseInsensitive)) //Check to see if otext has the word from the first column of dataArray --- I believe that this is the problem
{
dataArray[iii][1] = "yes"; //if it does, it writes yes in the next column
}
else
{
dataArray[iii][1] = "no"; //if it doesn't, it writes no in the next column
}
if (dataArray[xxx][0] == "-1") // checks the next row in the first column to see if it is the placeholder "-1". If it is, it breaks the loop.
break;
}



}


QString cdata::debugresults()
{ //formats the array into a string that will eventually become the csv file. For now, it formats them and returns the formatted string to start(), which returns that to on_parse_B_clicked() and displays it.
QString tmp1 = "Test results:,\n";
for (int iii=0; iii<192; iii++)
{

QString tmp2 = dataArray[iii][0];
QString tmp3 = dataArray[iii][1];
tmp2.append(" , " + tmp3 + " , ");
tmp1.append(tmp2 + " \n");

int xxx = iii+1;

if (dataArray[xxx][0] == "-1")
break;

}
return tmp1;
}

wysota
11th April 2011, 17:32
I suggest you avoid calling your variables 'xxx' and 'tmp2', it makes your code completely unreadable.

Basically the easiest way to count words in the text is to use what QTextStream offers:

QHash<QString,int> occurences;
QFile f("input");
if(!f.open(QFile::ReadOnly|QFile::Text)) return false;
QTextStream stream(&f);
QString word;
QRegExp isWord("[A-Za-z_]+"); // allow letters and underscore only
while(!stream.atEnd()){
stream >> word; // get next word
if(!isWord.exactMatch(word)) continue; // doesn't match, skip it
int cnt = occurences.value(word, 0);
occurences.insert(word, cnt+1);
}
Now "occurences" contains count of all words in the text and you can choose the ones you need. You can also pre-insert all the words you're interested in into the hash and count only those that are already in the hash. The representation is superflous but it's simplest what you can do. An advanced approach would use a prefix tree instead of the hash to make the representation much more compact.

high_flyer
11th April 2011, 17:42
if you put a break point in line 89, what are the values you see for otext and dataArray[iii][0]?

TomJoad
11th April 2011, 17:53
if you put a break point in line 89, what are the values you see for otext and dataArray[iii][0]?

I was having a hard time figuring out what you were referring to. Do you mean this function?


void cdata::parseInput(QString otext)
{//checks to see if the country dataArray[iii][0] is listed in otext

for (int iii = 0; iii<192; iii++)
{

int xxx = iii+1;

if (otext.contains(dataArray[iii][0], Qt::CaseInsensitive)) //I believe that this is the problem
{
dataArray[iii][1] = "yes";
}
else
{
dataArray[iii][1] = "no";
}
if (dataArray[xxx][0] == "-1")
break;
}


For this one, I expect it to check to see if dataArray[iii][0] is in otext. If it is, I expect it to write "yes" to dataArray[iii][1], if not, it should write no. Once it is done with that if else statement, it moves to the next if statement which checks if dataArray[xxx = iii+1][0] is "-1". If it is, the loop breaks and the program stops checking. If not, it cycles through the loop again to check the next value of dataArray[iii][0].

The first column of dataArray as populated by popcountries():
USA
Ireland
Italy
France

debug output regardless if "USA" is otext or not:
Test results:,
USA , no ,
Ireland , no ,
Italy , no ,
France , no ,

What it should be if USA is otext:
Test results:,
USA , yes ,
Ireland , no ,
Italy , no ,
France , no ,

wysota
11th April 2011, 18:01
Is there any particular reason why you are doing it the hard way? There are so many beautiful data structures in the world that suit your purpose better than a flat array - like a "set", for example.

QSet<QString> allCountries;
QList<QIODevice*> streamsToCheck;
struct Bucket {
QIODevice *device;
QSet<QString> found;
QSet<QString> notFound;
};
QList<Bucket> buckets;
foreach(QIODevice *dev, streamsToCheck) {
Bucket b;
b.notFound = allCountries;
b.device = dev;
QTextStream stream(dev);
QString word;
while(!b.notFound.isEmpty() && !stream.atEnd()) {
stream >> word;
word = word.toLower();
if(b.notFound.contains(word))
b.notFount.remove(word);
}
b.found = allCountries - b.notFound;
buckets << b;
}

TomJoad
11th April 2011, 20:44
Ok everyone, I have narrowed down the problem. It is actually a problem with oText() not returning like it should. Perhaps I am not accessing it right (I don't think so), or something else is going wrong. If I hard code input (input = "france";) it works.


QString start()
{
//calls cdata class from above
cdata d;

d.popcountries(); // populates first column of the array from the given file

QString input;
MainWindow mw; //calls mainwindow class
input = mw.oText(); //returns the text to be searched through as the user's input

d.parseInput(input); //uses the user's input to see if it contains dataArray[iii][0] (the first column of the array)

QString tmp; //creates a qstring to return the final/debug results
tmp = d.debugresults(); //debug results. This is used to format the final results as they would appear in the csv file.

return tmp; //returns the formatted final/debug results to on_parse_B_clicked()

}

oText:

QString MainWindow::oText()
{ //grabs the user's input and returns it to start()

QString otext;
otext = ui.tmpTextinput->toPlainText();
return otext;
}

Complete header:




#include "ui_mainwindow.h"

#include <QMainWindow>
#include <QFile>
#include <QTextStream>
#include <QString>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);

QString oText();


private slots:
void on_parse_B_clicked();

private:
Ui::MainWindow ui;
};


class cdata
{
public:
void popcountries();
QString debugresults();
void parseInput(QString);

private:
QString dataArray[192][192];

};

Are you not allowed to return a QString like this? It doesn't seem likely....

========================

FIXED (in a roundabout way):

Still didn't figure out why oText() wasn't returning the QString correctly, but fixed it by adding the correct lines of code to the push button and then passing it to start().


QString start(QString input) //<---changed
{
//calls cdata class
cdata d;

d.popcountries(); //in cdata.cpp

d.parseInput(input); //parses the input and in parseInput.cpp

QString tmp;
tmp = d.debugresults(); //debug results

return tmp;

}


void MainWindow::on_parse_B_clicked()
{

QString tmp;
QString input = ui.tmpTextinput->toPlainText(); //<-- added to get the contents of the txt input
tmp = start(input); //<-- passes the input to start
ui.debugResultsTxt->setPlainText(tmp);
}

Anyone happen to know the reason why oText() wasn't returning the QString correctly?

ChrisW67
12th April 2011, 05:31
Where does one start.



QString start()
{
//calls cdata class from above
cdata d;

d.popcountries(); // populates first column of the array from the given file

QString input;
MainWindow mw; //calls mainwindow class
input = mw.oText(); //returns the text to be searched through as the user's input

d.parseInput(input); //uses the user's input to see if it contains dataArray[iii][0] (the first column of the array)

QString tmp; //creates a qstring to return the final/debug results
tmp = d.debugresults(); //debug results. This is used to format the final results as they would appear in the csv file.

return tmp; //returns the formatted final/debug results to on_parse_B_clicked()

}

OK. At line 9 you create a new instance of the MainWindow class. You never show this instance, so any UI element in that class will have its default value, e.g. an empty string for line and text edits. At line 10 you retrieve the default value of a text edit via oText() into input and process it. oText() is functioning correctly.