0

I'm trying to load a text file with the values: 10 11 12 13 14 15 16 17 18 19 20 30 40 50 55 60 70 80 90 91 92 93 94 95 96 97 98 99 into a linked list, inserting each new value into the end of the list. The issue I'm having is that when I run the code, I get the error that I'm trying to run a string through a function that expects an int, which makes sense, but once I added stoi() into the mix to convert the values to int, I started getting crazy amounts of errors.

I've been working on this function for the past day or so, and none of my searching as yielded any results. I feel that I'm pretty close with this, but I'm probably missing something big here. Still pretty new to linked lists as a whole, we just learned about them in class last week.

#include <iostream>
#include <fstream>
#include <string>
#include "linkedlist.h" // Has the prototypes for each function
using namespace std;
// I didn't include a lot of functions since I don't think they're
// related to the error, but let me know if I should

Node* createNewNode(int data) {
    Node *ptr;
    Node *temp = new Node();
    temp -> data = data;
    temp -> next = NULL;
    ptr = temp;
    return ptr;
}

Node* createNewList() {
    Node *head = NULL;
    return head;
}

Node* load(string filename) {
    Node *head = createNewList();
    string num;
    ifstream myfile(filename.c_str());
    while(myfile >> num) { // looping through each number
        int num1 = stoi(num); // Converting string to int
        myfile << insertAtEnd(head, num1);      
    }
    return head;
}

void insertAtEnd(Node *list, int data) {
    Node *ptr = createNewNode(data);
    if (list == NULL) {
        list = ptr;
     }
     else { 
         Node *temp = list;
         while(temp -> next != NULL) {
             temp = temp -> next;
         }
         temp -> next = ptr;
    }
}

int main() {
    load("../resource/listdata.txt"); // name/location of the file
    //No errors from rest of code but I can post if necessary
}

There are far too many errors for me to just paste them here, but I took a screenshot of most of them here: https://i.stack.imgur.com/RWCbV.png .

Thank you in advance for any help you can give me!

Edits:

Node* load(string filename) {
    Node *head = createNewList();
    string num;
    ifstream myfile(filename.c_str());
    while(myfile >> num) { // looping through each number
        int num1 = stoi(num); // Converting string to int
        insertAtEnd(head, num1);      
    }
    myfile.close();
    return head;
}

There are no longer any compiling errors, though when the code runs it outputs: 0 0 NULL exit status -1

If I had to guess, I would assume my issue is now the while(myfile >> num) area, because I don't think the code is properly traversing the text file and using the numbers, though I'm not sure about that.

Edit 2:

Node *load (string filename) {
  Node *head;
  string num;
  ifstream myfile(filename.c_str());
  while(myfile >> num) {
    if(head) {
      int num1 = stoi(num);
      insertAtEnd(head, num1);
    } 
    else {
      head = createNewList();
      int num1 = stoi(num);
      head = createNewNode(num1);
    }
  }
  myfile.close();
  return head;
}

I'm hoping I followed the instructions correctly, though there's a good chance I haven't... I'm getting the same message as above, being 0 0 NULL exit status -1 but no errors still, which is good and bad, as I'd love to see what isn't working right now.

Spago
  • 69
  • 1
  • 1
  • 6
  • 1
    The **first** error message is usually the most important one. After that, error messages can be caused by the compiler trying to recover from the first one by introducing something that turns out not to work. So post the **first** error message, along with anything marked "note" that pertains to it. – Pete Becker Feb 11 '19 at 20:17
  • Why would you A) ever use a linked list rather than a `std::vector`? B) Implement your *own* list rather than just using `std::list` (but *really*, just use `std::vector` already)? A linked list is a *horrible* data structure to expose a modern CPU/memory subsystem to. – Jesper Juhl Feb 11 '19 at 20:18
  • It would probably be more usual, in this kind of exercise, to insert at the *beginning* of the list. Perhaps you have a specific reason not to do so but, otherwise, are you sure that you would not rather insert at the beginning? – thb Feb 11 '19 at 20:19
  • 2
    `insertAtEnd` returns `void`. It is this `void` that `>>` is going to try to store data in. Discuss with [your Rubber Duck](https://en.wikipedia.org/wiki/Rubber_duck_debugging) whether or not this is a good idea. – user4581301 Feb 11 '19 at 20:19
  • Don't use `NULL`, please. Use `nullptr`. – Jesper Juhl Feb 11 '19 at 20:21
  • @JesperJuhl You overstate. Vectors are often better but lists are fine. Besides, the exercise of building one's own list is quite educational. (Fashion says, lists are the new goto.) – thb Feb 11 '19 at 20:21
  • @thb Sure, if you *need* the property of a list of never moving an element in memory, then *sure*, use it. But for any other reason I'll bet that a `vector` will perform better - *even* if you have to sort it. Don't underestimate the cost of chasing pointers all over memory, cache misses and what the prefetcher can do for you. A challenge; give me *one* use case of 10000 elements or more where `list` out performs `vector` (where you don't *need* the "does not move in memory" guarantee)..? – Jesper Juhl Feb 11 '19 at 20:26
  • @JesperJuhl No, you are right. Vectors are faster in my experience. Less elegant, but faster. They are even faster when `reserve()` is impractical to call. I just think that recent pogroms against the good old list are overheated. That's all. Lists are cool. – thb Feb 11 '19 at 20:29
  • @JesperJuhl Hey, since you're a CPU-architecture man, consider [this question](https://stackoverflow.com/q/54637462/1275653) which I have recently asked. One suspects that you might be able to write a good answer to it, were you so inclined. – thb Feb 11 '19 at 20:36
  • 1
    @thb cache coherency questions are difficult - not going to bite on that one. ;) – Jesper Juhl Feb 11 '19 at 20:39
  • Hi @JesperJuhl , thanks for the comments. This is actually an assignment for one of my classes, and most of the things I'm doing (using linked list, inserting at the end) are all instructions given to me by my professor (I assume so we can better grasp the idea behind it). I have yet to learn vectors myself, and I'm not sure if I'd be allowed to use them instead anyway... – Spago Feb 11 '19 at 21:04
  • @PeteBecker That makes sense. The first error is: `no match for operator>> {operand types are std::ifstream {aka std::basic_ifstream} and void : myfile >> insertAtEnd(head, num1);` So I assume there's some sort of problem with my use of ifstream with the insertAtEnd function inside the load function? I'm not 100% on what I can do to fix this though. – Spago Feb 11 '19 at 21:08
  • Surprisingly, I do not see anyone else working on answering this question yet, so I might try, provided that you are still online. What is the line `myfile << insertAtEnd(head, num1);` supposed to do, in your view? I ask because it looks to me as though `insertAtEnd()` returned `void`. – thb Feb 11 '19 at 21:30
  • 1
    @thb Thank you for continuing to try to help! Your comment made me think about what I had written, and yeah, that part didn't make sense. I removed the `myfile >>` before `insertAtEnd()` and that removed all the errors I was getting. What that did do, however, is it gave me this output: `0 0 NULL exit status -1'. I'm not entirely sure what happened there, and it's not giving me any other errors... I'm about to edit my main post to show the changes I've done, those should be up in just a minute. – Spago Feb 11 '19 at 21:40

2 Answers2

1

I'd create a class to keep track of head and all the functions concerning one list of nodes. I'll call it NodeList. I'd also add a pointer to the last node in the list for convenience and speed.

#include <iostream>
#include <fstream>
#include <string>

struct Node {
    Node* next;
    int data;
};

class NodeList {
    Node* head;
    Node* last;
public:
    // default constructor - an empty list
    NodeList() : head(nullptr), last(nullptr) {}

    // construction using a filename
    NodeList(const std::string& filename) : NodeList() {
        load(filename);
    }

    // deleted copy & move ctors and assignment operators for simplicity
    NodeList(const NodeList&) = delete;
    NodeList(NodeList&&) = delete;
    NodeList& operator=(const NodeList&) = delete;
    NodeList& operator=(NodeList&&) = delete;

    // destructor    
    ~NodeList() {
        clear();
    }

    // go through all Nodes and delete them
    void clear() {
        Node* curr = head;
        Node* next;
        while(curr) {
            next = curr->next;
            delete curr;
            curr = next;
        }
        head = nullptr;
        last = nullptr;
    }

    // load data from a file
    void load(const std::string& filename) {
        clear();
        append(filename);
    }

    // append data from a file
    void append(const std::string& filename) {
        std::ifstream is(filename);
        is >> *this;   // using operator>> further down
    }

    // find a node by value      
    Node* find(int data) const {
        Node* curr = head;
        while(curr && curr->data != data) curr = curr->next;
        return curr;
    }

    // add a node last in the list    
    Node* add(int data) {
        Node* nn = new Node{nullptr, data};
        if(last) { last->next = nn; last = nn; }
        else { head = last = nn; }
        return nn;
    }

    // delete a node by supplying a Node*    
    void del(Node* n) { // delete a certain node
        if(n==nullptr) return;

        if(head==n) {
            if(last==n) head = last = nullptr;
            else head = head->next;
        } else {
            Node* curr = head;
            do {
                if(curr->next==n) {
                    curr->next = n->next;
                    break;
                }
                curr = curr->next;
            } while(curr);
        }
        delete n;
    }

    void del(int data) { // delete a Node by value
        del(find(data));
    }

    // operator>> to populate the NodeList from an istream
    friend std::istream& operator>>(std::istream&, NodeList&);

    // operator<< to stream all values in the NodeList to an ostream
    friend std::ostream& operator<<(std::ostream&, const NodeList&);
};

// add nodes from stream
std::istream& operator>>(std::istream& is, NodeList& nl) {
    int tmp;
    // no need for std::stoi(), just stream into an int
    while(is >> tmp) nl.add(tmp);
    return is;
}

// output nodes to stream
std::ostream& operator<<(std::ostream& os, const NodeList& nl) {
    Node* curr = nl.head;
    while(curr) {
        os << curr->data << " ";
        curr = curr->next;
    }
    return os;
}

int main() {
    NodeList nl("listdata.txt");
    std::cout << nl << "\n";

    Node* p = nl.find(40);
    nl.del(p);  // delete the Node found above
    nl.del(10); // delete the first Node
    nl.del(99); // delete the last Node

    std::cout << nl << "\n";
}

Output (given the data in your post):

10 11 12 13 14 15 16 17 18 19 20 30 40 50 55 60 70 80 90 91 92 93 94 95 96 97 98 99
11 12 13 14 15 16 17 18 19 20 30 50 55 60 70 80 90 91 92 93 94 95 96 97 98
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • Sure, Ted, that is a good solution, +1. However, as OP has explained in comments, his solution labors under extra constraints imposed by professor. This OP does not seem to be your typical Stack Overflow do-my-homework-for-me scummer, but is actually working the problem. We're just coaching him along a bit. (For beginner's code, actually, if you look closely at it, OP's code is not bad, really. It shows some real thought. It only needs him to straighten out a few details. He has nearly got it.) – thb Feb 11 '19 at 22:33
  • 1
    You are right and if OP is anything like me, some of it may spark an idea or two that could be incorperated in the current sulution and the rest will be thrown away. The main thing was to create a support class for keeping the data and functions for one list. – Ted Lyngmo Feb 11 '19 at 22:40
  • Thank you for your answer @TedLyngmo ! But yes as thb has said, I am restricted a little bit in what I can do by my professor, as well as a lot of what you've got there has gone a bit over my head. I'm sure we'll be covering those topics soons, but even classes themselves are pretty new to me. – Spago Feb 11 '19 at 22:48
  • @Spago Sure thing! And as thb said, you have come far so you'll add functions to your `struct`s/`class`es in no-time. A function in a class is similar to the functions you've written, it's just that instead of giving the function a pointer to the object it should be working with, a class function knows (via the special pointer `this`). So, instead of `function(object);` you'll get `object.function();` (or `object->function();` if you're dealing with pointers). – Ted Lyngmo Feb 11 '19 at 23:00
0

Your code shows good thought, so consider: the pointer head is supposed to point to the list's leading element, is it not? However, the only lines in your code that assign a value to head assign NULL (should be nullptr, incidentally).

That would be a problem.

Since altering the return type of insertAtEnd() is in your problem forbidden, we should call that function only when head already has a value, as

    if (head != nullptr) {
        // call insertAtEnd()
    }
    else {
        // do something to start the list and, incidentally,
        // to assign a value to head
    }

Actually, that's a beginner's way to write it. More fluent would be

    if (head) {
        // ...

which means the same thing.

Anyway, if you do it this way, you can and probably should make your insertAtEnd() function simpler because it will no longer need to handle the case of an empty list.

[...]

Now you have worked some more. Your output is still not what you expect, so how to debug it? When you have a problem but are unsure where in your code the bug is, how should you go about localizing it? That is, how should you figure out on which line the trouble arises? The program is too big to find the problem merely by staring at the code!

To debug, I would try something like this:

Node *load (string filename) {
  Node *head;
  string num;
  ifstream myfile(filename.c_str());
  cerr << "diagn 100\n";
  while(myfile >> num) {
    cerr << "diagn 150\n";
    if(head) {
      cerr << "diagn 200, head == " << head << "\n";
      int num1 = stoi(num);
      insertAtEnd(head, num1);
      cerr << "diagn 250\n";
    } 
    else {
      cerr << "diagn 300, head == " << head << "\n";
      head = createNewList();
      cerr << "diagn 325, head == " << head << "\n";
      int num1 = stoi(num);
      head = createNewNode(num1);
      cerr << "diagn 350, head == " << head << "\n";
    }
  }
  myfile.close();
  cerr << "diagn 900, head == " << head << "\n";
  return head;
}

Most likely, most of those outputs to the error stream will tell you nothing you did not already know, but one or more of the outputs might look wrong. When you find the one that looks wrong, if any, that will tell you where to focus your debugging attention.

(The "diagn," incidentally, stands for "diagnostic." I like "diagn" because no other English word I use has those letters, so it's easy to search for in program text.)

Regarding the error stream, std::cerr, this by default sends output to the same place std::cout does. However, it is possible to divert the one stream or the other, or both, sending the two to different places. Exactly how to divert depends on which system (Debian, Windows, OSX, etc.) you are using, but the diversion usually isn't hard to make.

thb
  • 13,796
  • 3
  • 40
  • 68
  • 1
    Thanks for your answer! I agree that having it return a value would make sense, but my professor gave us the prototypes for this assignment, and `insertAtEnd()` was made to be a void function by him, for whatever reason, and I doubt I could change it on him... Thank you for the tip on `NULL` though, I've changed that accordingly. – Spago Feb 11 '19 at 22:14
  • Aha, I see. The `insertAtEnd()` must return `void`, must it? Then let us see how else to assign `head` a value.... – thb Feb 11 '19 at 22:17
  • Thank you for your continued help @thb , I've edited my post above with some new code, hopefully I followed your instructions correctly, let me know! – Spago Feb 11 '19 at 22:55
  • Thank you for the tip on debugging @thb ! I've never seen 'cerr' before so that is very useful! So what I found by doing this is that it skips the while loop altogether, so clearly there's something wrong there. For whatever reason the condition 'myfile >> num' is being met the first time around, but I'm unsure as to why. Maybe that's the wrong way to be traversing the text file, I'm not entirely sure. Will do some more research on that right now, if you have any extra information on that it would be greatly appreciated! – Spago Feb 11 '19 at 23:58
  • You're on the track now. The remaining problem is not major. You'll soon isolate it. Since you're new on Stack Overflow, when you're done with these answers, etiquette here is (i) *to upvote* all answers you have found useful by clicking the orange upward-pointing arrow to the left of each such answer; and also (ii) *to accept* the one answer you have found most useful but clicking the green check beneath the orange arrow. It won't take you long, now, I suspect, to fix the last bugs. I'm off to bed. – thb Feb 12 '19 at 00:08