【问题标题】:In a loop, different strings read are being repeated as the same string in the end在一个循环中,读取的不同字符串最终被重复为相同的字符串
【发布时间】:2016-10-12 02:05:36
【问题描述】:

为了更好地解释它,我有一个结构“用户”,其中包含用户 ID、他们的姓名和一个包含他们朋友 ID 的数组。循环读取一个文件作为输入,其中每一行都包含有关用户的信息,在此模型中:“ID;name;friendsID”,并且用户可能有多个朋友(在本例中为“ID;name;friendID1 ;...;friendIDx")。

我试图读取每个用户的每一行,计算有多少“;”需要知道用户有多少朋友,并使用 strtok 获取用户 ID、姓名和朋友的 ID。一切正常,除了名字。它正确读取了每个名称,但不知何故,吃掉了循环的结尾,每个用户都有相同的名称,读取的姓氏。

如果它在循环内正确读取和归属名称字符串,我不知道为什么会这样做。

这是我目前所得到的:

typedef struct user {
   int ID;
   char* name;
   int* friendsID;
}; 

// declaring variables, declaring user array, opening file, allocating memory, etc.

for(i = 0; i < num_users; i++){
    fgets(line, sizeof(line), input); // Reads a line
    for(j = 0, user_friends = 0; line[j]; j++) { // Counts how many friends the user has
       user_friends += (line[j] == ';');
    }
    user[i].ID = atoi(strtok(line, ";")); // Reads the ID
    user[i].name = strtok(NULL, ";"); // Reads the name
    for(k = 0; k < user_friends - 1; k++) { // Reads the friend's IDs
       user[i].friendsID[k] = atoi(strtok(NULL, ";")); 
    }
}

最后,我得到了所有正确的 ID 和朋友的 ID,但名字都一样。

【问题讨论】:

  • 更多代码,包括类型定义,会有所帮助...
  • line 会在每次调用 fgets 时被覆盖。由于user[i].name 是指向line 中的标记的指针,因此它们都将指向最后读取的line。 (如果ID 的长度不同,那么它们将指向最后一行的不同部分)
  • 我打赌user[i].name 不是字符串。 user[i].name的类型是什么?
  • 哦,现在说得通了。所以我每次都必须使用不同的“线”,然后呢?
  • 或者至少user[i].name = strdup (strtok(NULL, ";")); (你将负责freeing 每个user[i].name 使用相同的line 没有错,你只需要复制到保留每个user[i].name。您还必须分配给friendsID[k]

标签: c string strtok


【解决方案1】:

代码可能会在许多区域调用未定义的行为。首先,只是一个 nit,但 * 与变量一起使用,而不是类型,例如:

typedef struct user {
    int ID;
    char *name;
    int *friendsID;
};

这通常可以防止以下声明:

int* a, b, c;    /* b and c are NOT pointers */

在您的代码中,您可以根据需要将';' 硬编码为strtok,但您最好创建一个常量或保存您的分隔符的变量,因此如果您需要调整它,你只在一个地方做。

注意,由于您同时拥有namefriendsID,它们都是未初始化的指针,并且没有存储多个字符或整数的空间。您必须为要存储的任何字符串或整数数组动态分配存储空间。

不需要for (i = 0; i &lt; num_users; i++)。正如您所解释的,每行都有一组user 的数据,因此只需读取所有行,除非您绝对需要将读取限制为特定行数。如果您想阅读所有行,只需一个简单的while (fgets (line, sizeof line, input)) 即可。

如 cmets 中所述,如下所示,您需要验证每次读取以及从行中获取的每个令牌。最简单的方法是检查返回,就像对每个函数调用所做的那样。

如果您有 friendsID 的存在,您需要一种方法来 (1) 测试是否有,以及 (2) 根据需要分配/重新分配,直到所有内容都被读取。以下是根据发布的有限信息解决您的问题的一种方法。它使用idx 作为user 索引和fidx 作为friendsID 索引(我没那么有创意)。将这些部分放在一起,您可以执行以下操作。通读 sn-p 并查看逻辑结构。 (注意:这只是一个示例,尚未发布任何数据)如果您有任何问题,请告诉我。

char *delim = ";\n";
int idx = 0, fidx = 0, nfriends = 10;

/* line must be declared as a character array (not pointer) to use sizeof
 * (only add record if both ID and name present)
 */

while (fgets (line, sizeof line, input)) {
    char *p = strtok (line, delim);
    if (p)  /* valid token for ID? */
        user[idx].ID = atoi (p);
    else {
        /* throw error */
        continue;
    }
    if ((p = strtok (NULL, delim)))  /* valid token for name? */
        user[idx].name = strdup (p);
    else {
        /* throw error */
        continue;
    }
    if ((p = strtok (NULL, delim))) { /* is there a friendsID? */
        /* allocate for nfriends */
        user[idx].friendsID = calloc (nfriends, sizeof *(user[idx].friendsID));
        if (!user[idx].friendsID) {
            /* memory exhausted, throw error */
            break;
        }
        user[idx].friendsID[fidx++] = atoi (p);  /* assign 1st ID */
        while ((p = strtok (NULL, delim))) {     /* loop for all remaining */
            user[idx].friendsID[fidx] = atoi (p);
            if (fidx++ == nfriends) {            /* check against limit */
                /* realloc user[idx].friendsID to 
                 * 2 * nfriends * sizeof *(user[idx].friendsID)
                 * nfriends *= 2
                 */
            }
        }
        /* optional -- realloc user[idx].friendsID to
         * fidx * sizeof *(user[idx].friendsID) for exact size
         */
    }
    idx++;  /* increment user index */
}

【讨论】:

  • 感谢您的完整回答!这很有趣,我稍后会尝试实现类似的东西(现在已经很晚了)。我唯一要补充的是,在文件中,关于用户的行下方会有更多信息。实际上,第一行是用户数,所以我可以知道有多少行关于用户(ID、姓名和朋友)。所以这就是我使用“for”的原因。很抱歉不太清楚,下次我在这里问问题时已经吸取了教训。
  • 当然,那是 100% OK,这就是我放弃笔记的原因(“如果绝对必要的话”)。然后您可以简单地阅读该行并执行while (nlines &lt; limit &amp;&amp; fgets (line, sizeof line, input)) { ... nlines++; } 这将负责将您处理的行数限制为limit
猜你喜欢
  • 2013-09-10
  • 1970-01-01
  • 2014-11-03
  • 2018-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-11
  • 1970-01-01
相关资源
最近更新 更多