代码之家  ›  专栏  ›  技术社区  ›  Kumar Roshan Mehta

strtok\u r导致分段错误

  •  2
  • Kumar Roshan Mehta  · 技术社区  · 6 年前

    此代码用于解析csv文件,但会导致分段错误。 我可以在这里看到类似的代码: Nested strtok function problem in C

    它们看起来一样,但我的代码导致了分段错误。为什么?

    #include <cstdlib>
    #include <string.h>
    #include <stdio.h>
    #include <vector>
    
    using namespace std;
    
    struct inputTuple{
        int user, item, rating;
    };
    
    int main(void)
    {
        char xdata[] = "1,88,0,874965758;1,2,1,876893171;1,99,1,878542960;";
        vector<inputTuple> input;
    
                int maxUser = 0, maxItem = 0;
            int user, item, rating;
    
            char *end_str;
            char *data_point = strtok_r(xdata, ";", &end_str);
    
            while(data_point != NULL) {
                char *end_attr;
                char *data_point_attr = strtok_r(data_point, ",", &end_attr);
                while(data_point_attr != NULL) {
    
                    user = atoi(data_point_attr);
                    data_point_attr = strtok_r(NULL, ",", &end_attr);
                    item = atoi(data_point_attr);
                    data_point_attr = strtok_r(NULL, ",", &end_attr);
                    rating = atoi(data_point_attr);
                    strtok_r(NULL, ",", &end_attr);
                    input.push_back({user, item, rating});
                    maxUser = max(maxUser, user);
                    maxItem = max(maxItem, item);
                    }
                data_point = strtok_r(NULL, ";", &end_str);
            }
        return 0;
    }
    
    3 回复  |  直到 6 年前
        1
  •  2
  •   Jean-François Fabre Darshan Ambre    6 年前

    您的内部循环是不需要的,并且对您的程序有害。

    内部循环提取3个标记,然后继续,提取第4个标记,但达到 NULL 在中间,以及 atoi 已通过 无效的 :segfault。

    您只需要一个循环(添加 assert 但理智声明):

        while(data_point != NULL) {
            char *end_attr;
            char *data_point_attr = strtok_r(data_point, ",", &end_attr);
    
                user = atoi(data_point_attr);
                data_point_attr = strtok_r(NULL, ",", &end_attr);
                assert(data_point_attr != NULL);
                item = atoi(data_point_attr);
                data_point_attr = strtok_r(NULL, ",", &end_attr);
                assert(data_point_attr != NULL);
                rating = atoi(data_point_attr);
                strtok_r(NULL, ",", &end_attr);
                input.push_back({user, item, rating});
                maxUser = max(maxUser, user);
                maxItem = max(maxItem, item);
    
            data_point = strtok_r(NULL, ";", &end_str);
        }
    

    更好的解决方案是使用真正的C++解决方案来拆分字符串。

        2
  •  2
  •   Abhijit Pritam Dutta    6 年前

    让·弗朗索瓦是对的。只需在调用之前检查值 atoi 如下所示。你每次打电话都要这么做 字符串转换为整数

    if(data_point_attr != NULL)
        user = atoi(data_point_attr);
    
        3
  •  2
  •   John Bollinger    6 年前

    根据Valgrind的说法,你的程序在这里失败了:

                    item = atoi(data_point_attr);
    

    ,因为函数参数为NULL。这似乎与让·弗朗索瓦的预测一致。使用gdb进行的一点调试表明,它发生在第二次循环迭代中,尽管您可以通过print语句调试来确定这一点。用gdb进行更多的调试就可以显示出发生了什么:您的内部循环有故障,因为当您到达记录的末尾时,循环条件不会变为false。

    你到底需要那个内环做什么?没有什么您通过一个单独的语句读取每个值,因此有效地展开了循环 vs。 在程序中需要消耗什么 任意的 CSV。 检查每个 strtok_r() 调用,但删除不需要的内部循环。