【问题标题】:Multiple conversion errors while trying to load text file into a linked list尝试将文本文件加载到链接列表时出现多个转换错误
【发布时间】:2019-02-11 20:13:24
【问题描述】:

我正在尝试将具有以下值的文本文件加载: 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 到链接列表中,插入每个新值都放入列表的末尾。我遇到的问题是,当我运行代码时,我得到一个错误,我试图通过一个需要 int 的函数运行一个字符串,这是有道理的,但是一旦我将 stoi() 添加到混合中将值转换为 int,我开始收到大量错误。

过去一天左右我一直在研究这个功能,但我的搜索都没有产生任何结果。我觉得我很接近这个,但我可能在这里遗漏了一些重要的东西。链表作为一个整体还是很新的,我们上周刚刚在课堂上了解了它们。

#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
}

错误太多了,我无法将它们粘贴在这里,但我在这里截取了大部分错误:https://i.imgur.com/GtXZ5oy.png

提前感谢您能给我的任何帮助!

编辑:

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

不再有任何编译错误,但在代码运行时会输出: 0 0 NULL exit status -1

如果我不得不猜测,我会假设我现在的问题是while(myfile &gt;&gt; num) 区域,因为我认为代码没有正确遍历文本文件并使用数字,尽管我不确定。

编辑 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;
}

我希望我正确地按照说明进行操作,尽管我很有可能没有...因为我很想看看现在什么不起作用。

【问题讨论】:

  • first 错误消息通常是最重要的。在那之后,错误消息可能是由于编译器试图通过引入一些结果无法正常工作的东西来从第一个消息中恢复而导致的。因此,请发布 first 错误消息,以及与之相关的任何标记为“note”的内容。
  • 你为什么 A) 曾经使用链表而不是 std::vector? B) 实现您的自己的 列表,而不是仅仅使用std::list(但真的,已经使用std::vector)?链表是一种可怕的数据结构,用于暴露现代 CPU/内存子系统。
  • insertAtEnd 返回void&gt;&gt; 将尝试存储数据的正是这个void。与your Rubber Duck 讨论这是否是个好主意。
  • @thb 缓存一致性问题很难 - 不会咬那个。 ;)
  • @thb 感谢您继续尝试提供帮助!你的评论让我想起了我写的东西,是的,那部分没有意义。我在insertAtEnd() 之前删除了myfile &gt;&gt;,这消除了我遇到的所有错误。然而,这样做的结果是它给了我这个输出:'0 0 NULL exit status -1'。我不完全确定那里发生了什么,并且它没有给我任何其他错误......我将编辑我的主要帖子以显示我所做的更改,这些应该会在一分钟内完成。跨度>

标签: c++ struct linked-list text-files


【解决方案1】:

我会创建一个类来跟踪head 以及与一个节点列表有关的所有函数。我会称之为NodeList。为了方便和速度,我还添加了指向列表中最后一个节点的指针。

#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";
}

输出(给定您帖子中的数据):

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,这是一个很好的解决方案,+1。然而,正如 OP 在 cmets 中解释的那样,他的解决方案在教授施加的额外限制下工作。这个 OP 似乎不是你典型的 Stack Overflow 为我做家庭作业的渣滓,但实际上正在解决这个问题。我们只是在指导他一点。 (对于初学者的代码,其实仔细看,OP的代码还不错,真的,有点真实的想法,只需要他理顺一些细节,差不多就懂了。)
  • 你是对的,如果 OP 和我一样,其中一些可能会引发一两个想法,可以将其纳入当前的解决方案中,其余的将被丢弃。主要是创建一个支持类来保存一个列表的数据和函数。
  • 感谢您的回答@TedLyngmo!但是是的,正如 thb 所说,我的教授对我能做的事情有点限制,而且你在那里得到的很多东西都让我有点不知所措。我相信我们很快就会涵盖这些主题,但即使是课程本身对我来说也很新。
  • @Spago 当然可以!正如 thb 所说,您已经走了很远,所以您将立即向您的 structs/classes 添加功能。类中的函数类似于您编写的函数,只是类函数知道(通过特殊指针this),而不是给函数一个指向它应该使用的对象的指针。因此,您将获得object.function();(或object-&gt;function();,如果您正在处理指针)而不是function(object);
【解决方案2】:

您的代码显示了很好的想法,因此请考虑:指针head 应该指向列表的前导元素,不是吗?但是,代码中唯一将值分配给 head 的行分配了 NULL(应该是 nullptr,顺便说一下)。

那会是个问题。

由于您的问题禁止更改 insertAtEnd() 的返回类型,因此我们应该仅在 head 已经有值时调用该函数,因为

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

其实这是初学者的写法。会更流畅

    if (head) {
        // ...

意思是一样的。

无论如何,如果您这样做,您可以而且可能应该使您的 insertAtEnd() 函数更简单,因为它不再需要处理空列表的情况。

[...]

现在你已经工作了一些。你的输出仍然不是你所期望的,那么如何调试呢?当您遇到问题但不确定代码中的错误在哪里时,您应该如何进行本地化?也就是说,你应该如何确定问题出现在哪条线上?程序太大,光看代码就发现问题了!

要调试,我会尝试这样的事情:

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

最有可能的是,错误流的大多数输出​​不会告诉您任何您不知道的信息,但其中一个或多个输出可能看起来有问题。当你发现一个看起来有问题的地方(如果有的话)时,它会告诉你应该把注意力集中在哪里。

(顺便提一下,“diagn”代表“diagnostic”。我喜欢“diagn”,因为我使用的其他英文单词都没有这些字母,所以很容易在程序文本中搜索。)

关于错误流std::cerr,默认情况下这会将输出发送到std::cout 所做的相同位置。但是,可以将一个流或另一个流或两者转移,将两者发送到不同的地方。具体如何转移取决于您使用的系统(Debian、Windows、OSX 等),但转移通常并不难。

【讨论】:

  • 感谢您的回答!我同意让它返回一个值是有道理的,但是我的教授给了我们这个作业的原型,insertAtEnd() 被他设置为一个 void 函数,无论出于何种原因,我怀疑我可以在他身上改变它...不过,感谢您在 NULL 上的提示,我已经相应地进行了更改。
  • 啊哈,我明白了。 insertAtEnd() 必须返回 void,不是吗?然后让我们看看如何为head 赋值......
  • 感谢您一直以来的帮助@thb,我已经用一些新代码编辑了上面的帖子,希望我正确地按照您的说明进行操作,请告诉我!
  • 感谢@thb 的调试提示!我以前从未见过'cerr',所以这非常有用!所以我通过这样做发现它完全跳过了while循环,所以很明显那里有问题。无论出于何种原因,第一次遇到条件“myfile >> num”,但我不确定为什么。也许这是遍历文本文件的错误方式,我不完全确定。现在将对此进行更多研究,如果您有任何额外的信息,将不胜感激!
  • 你现在在轨道上。剩下的问题不大。你很快就会隔离它。由于您是 Stack Overflow 上的新手,当您完成这些答案后,这里的礼仪是 (i)赞成点击左侧的橙色向上箭头,您发现有用的所有答案每个这样的答案;以及 (ii) 接受您认为最有用的一个答案,但单击橙色箭头下方的绿色复选标记。现在,我怀疑你不会花很长时间来修复最后的错误。我要睡觉了。
猜你喜欢
  • 1970-01-01
  • 2014-07-28
  • 1970-01-01
  • 2011-04-02
  • 2010-12-30
  • 1970-01-01
  • 1970-01-01
  • 2014-11-19
  • 1970-01-01
相关资源
最近更新 更多