QString::split(" ") works only for the first element

I'm making a program to sort through a string of items separated by spaces. When I use the first element of listt (listt[0]) the program runs perfectly fine, however when I try anything other then listt[0] like listt[1] or listt[2] I get an error. Any input or advice would be greatly appreciated.

//text from text file
Brandan Janurary 1 2000 Math 7A 4 9 10 9 10 9 0 0 0 0 0 0 
Brandan Janurary 1 2000 Math 7A 4 9 10 7 10 10 0 0 0 0 0 0 
Brandan Janurary 1 2000 Math 7A 4 10 10 10 10 10 0 0 0 0 0 0 
Bob Janurary 1 2000 Math 7A 4 9 8 10 10 10 0 0 0 0 0 0 
Bob Janurary 1 2000 Math 7A 4 9 8 10 10 10 0 0 0 0 0 0 


//relevant part of code using listt[0] (works)
void MainWindow::on_pushButton_clicked()
{
    QString line, name;
    int avglist;
    QString *reallist;
    reallist =new QString[10000];
    QString *listof;
    QStringList listt;
    listof =new QString[10000];
    int x=0;

    name=ui->lineEdit->text();

    QFile file("C:/Users/brandan/Desktop/GUIPrograms/Kumonreal.txt");

    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QTextStream stream(&file);

        do
        {
        line = stream.readLine();
        listof[x] = line;
        qDebug() << line;
        x++;
        } while(!line.isNull());
    }

    for (int q=x; q>=0; q--)
    {
        QString Parsing;
        Parsing = listof[q];

        listt = Parsing.split(" ");

        qDebug() << listt[0]; //"name" in string
        reallist[q] = listt[0];


    }

    int Counter = sort(reallist,x,name);
    qDebug() << Counter;

}

Answers


Your code fails because of two specific bugs. I list them first. Other bugs don't make it immediately fail, but are bugs nevertheless.

  1. You're reading empty lines and the result of split() then has one element. You're trying to access non-existing elements and it fails.

  2. Your second loop starts at an invalid index into listof since you've incremented x too far. So, you try to access a default-constructed empty string because your x is one too large. If you happen to read 10,000 lines, your x will point past the end of the array: index 10,000 is invalid, last one is 9,999!

  3. You don't deallocate the memory allocated for the elements of the lists. Use smart pointers to help you out with that. You should use QScopedArrayPointer for arrays allocated with new Type[]. It is an error (even if it doesn't crash!) to use QScopedPointer on such arrays.

  4. Any extra spaces at the end of the line, or within the line, will be converted to spurious elements. Pass QString::SkipEmptyParts as a second argument to split.

  5. The code will fail if presented with an empty file. Your check for end of file is wrong, you should use stream.atEnd() instead.

  6. The code also will fail if the file cannot be opened: You should immediately exit when the file cannot be opened - there's no point to the rest of the code then, and it saves some indentation and makes it easier to read.

  7. The code will fail if presented with a file of more than 10,000 elements: You have fixed-size arrays.

  8. The code will fail if any line has less than 2 elements in it.

Fixing all of those bugs yields this horrible C++ code. It doesn't crash and burn, it works "correctly" for files with 10,000 entries or less, but it's still bad code:

void processDataOrg(QIODevice * dev)
{
    if (!dev->open(QIODevice::ReadOnly | QIODevice::Text)) return;

    const int N = 10000;
    QScopedArrayPointer<QString> listof(new QString[N]);
    QTextStream stream(dev);
    int x = -1; /* FIX for problem #2 */
    while (!stream.atEnd()) {
        QString line = stream.readLine();
        listof[++x] = line;  /* FIX for problem #2 */
        if (x == N-1) break;
    }

    QScopedArrayPointer<QString> reallist(new QString[N]);
    for (int q=x; q>=0; q--) {
        QString Parsing = listof[q];
        QStringList listt = Parsing.split(" ", QString::SkipEmptyParts);
        if (listt.size() < 2) continue; /* FIX for problem #1 */

        qDebug() << listt[0] << listt[1];
        reallist[q] = listt[0];
    }
}

Of course you wouldn't have those problems if you were using the lists as they were meant to be used! Appending to a list doesn't need you to keep track of any indices. Neither you need to worry about indices when iterating a list. You're coding like if it was C, not C++. Your code could be simplified as below.

void processDataBetter(QIODevice * dev)
{
    if (!dev->open(QIODevice::ReadOnly | QIODevice::Text)) return;

    QStringList lines;
    QTextStream stream(dev);
    while (! stream.atEnd()) {
        lines << stream.readLine();
    }

    QStringList names;
    foreach (QString line, lines) {
        QStringList elements = line.split(' ', QString::SkipEmptyParts);
        if (elements.size() < 2) continue;
        qDebug() << elements[0] << elements[1];
        names << elements[0];
    }
}

It still doesn't make any sense to keep the lines list, so:

void processDataOK(QIODevice * dev)
{
    if (!dev->open(QIODevice::ReadOnly | QIODevice::Text)) return;

    QStringList names;
    QTextStream stream(dev);
    while (! stream.atEnd()) {
        QString line = stream.readLine();
        QStringList elements = line.split(' ', QString::SkipEmptyParts);
        if (elements.size() < 2) continue;
        qDebug() << elements[0] << elements[1];
        names << elements[0];
    }
}

Since you seem to wish to sort the data by name, you can use QMap as a data structure that implicitly sorts its entries by key.

For our test, we can use a QBuffer instead of a QFile to have a self-contained, single-file example. The output is sorted by name:

"Bob" "Janurary" 
"Bob" "Janurary" 
"Brandan" "Janurary" 
"Brandan" "Janurary" 
"Brandan" "Janurary" 

Finally, a reasonably correct, self-contained example:

#include <QBuffer>
#include <QTextStream>
#include <QStringList>
#include <QMultiMap>
#include <QDebug>

QByteArray fileData(
    "Brandan Janurary 1 2000 Math 7A 4 9 10 9 10 9 0 0 0 0 0 0\n"
    "Brandan Janurary 1 2000 Math 7A 4 9 10 7 10 10 0 0 0 0 0 0\n"
    "Brandan Janurary 1 2000 Math 7A 4 10 10 10 10 10 0 0 0 0 0 0\n"
    "Bob Janurary 1 2000 Math 7A 4 9 8 10 10 10 0 0 0 0 0 0\n"
    "Bob Janurary 1 2000 Math 7A 4 9 8 10 10 10 0 0 0 0 0 0");

void processData(QIODevice * dev)
{
    if (! dev->open(QIODevice::ReadOnly | QIODevice::Text)) return;

    typedef QMultiMap<QString, QStringList> Map;
    Map map;

    QTextStream stream(dev);
    while(! stream.atEnd()) {
        QString line = stream.readLine().trimmed();
        QStringList elements = line.split(' ', QString::SkipEmptyParts);
        if (elements.count() < 2) continue;
        QString name = elements[0];
        map.insert(name, elements);
    }

    for (Map::const_iterator it = map.begin(); it != map.end(); ++it) {
        qDebug() << it.key() << it.value()[1];
    }
}

int main()
{
    QBuffer buf(&fileData);
    processData(&buf);
}

the return type of QString::split() is QStringList not QString*

so in your code should like this:

QStringList listt = Parsing.split(" ");

and access like

listt[0]
listt[1]
listt[2]...

Need Your Help

How do I reduce the Socialite scope in Laravel 5?

laravel google-api laravel-5 laravel-socialite

I'm successfully using Laravel 5's socialite feature, but I feel that it's asking for more permissions than makes sense for the end user when they sign up. For instance, Google tells my users that ...

cant get associative array variables from cookies in codeigniter

php arrays codeigniter cookies

i seem to be stuck at this problem for quiet a few time, basically i have used cookie in codeigniter, and passed an array with different names to different functions, the code to set the cookie is