c中的嵌套strtok导致无限循环
我让用户输入一个用户名,然后转到该文件并提取对应于特定用户的值。我知道问题在于我使用strtok的方式,因为它只对第一个用户有效 一旦找到用户,我想停止在文件中搜索c中的嵌套strtok导致无限循环,c,linux,C,Linux,我让用户输入一个用户名,然后转到该文件并提取对应于特定用户的值。我知道问题在于我使用strtok的方式,因为它只对第一个用户有效 一旦找到用户,我想停止在文件中搜索 int fd; fd=open(fileName,O_RDONLY,0744); if (fd==-1) { printf("The file userDetails.txt failed to open.\n"); exit(1); } int fileSize = sizeof(fileOutput)/siz
int fd;
fd=open(fileName,O_RDONLY,0744);
if (fd==-1)
{
printf("The file userDetails.txt failed to open.\n");
exit(1);
}
int fileSize = sizeof(fileOutput)/sizeof(fileOutput[0]); //size of file
printf("%d\n",fileSize);
int bytesRead = read(fd,&fileOutput,fileSize);
//TERMINATING BUFFER PROPERLY
fileOutput[bytesRead] = '\0';
printf("%s\n",fileOutput);
//READING LINE BY LINE IN FILE
char *line;
char *data;
char *name;
char *saltValue;
char *encryptedValue;
line = strtok(fileOutput,"\n"); //SPLIT ACCORDING TO LINE
while (line != NULL)
{
data = strtok(line, ":");
while (data != NULL)
{
name = data;
if (strcmp(name,userName)==0)
{
printf("%s\n","User exists");
saltValue = strtok(NULL,":");
printf("%s\n",saltValue);
encryptedValue = strtok(NULL, ":");
printf("%s\n",encryptedValue);
break;
}
else
{
break;
}
}
if (strcmp(name,userName)==0) //user found
{
break;
}
else //user not found
{
strtok(NULL,"\n");
}
}
如果您仅限于读取,则可以,但只能使用strtok一次\n来解析fileOutput中的每一行,而不能再次嵌套以解析“:”。否则,由于Strutk通过在找到的定界符中插入“0”来修改字符串,您将在行中写入NUL终止字符,这将导致外部Strutk考虑下一次迭代结束的字符串。
相反,在每一行上使用一个单独的指针和strhr行“:”来定位该行的第一个“:”,然后使用指向行开头的指针和指针定位“:”。例如,如果您有一个函数来检查文件中是否包含用户名,成功时返回0,失败时返回1,则可以执行以下操作:
...
for (char *line = strtok(fileOutput,"\n"); line; line = strtok (NULL, "\n"))
{
char *p = strchr (line, ':'); /* find first ':' */
if (!p) /* if not found, bail */
break;
if (strncmp (userName, line, p - line) == 0) { /* check name */
printf ("found user: %s hash: %s\n", userName, p+1);
return 0;
}
}
fputs ("user not found.\n", stdout);
return 1;
这可能是您可以采取的更简单的方法之一。如果您仅限于阅读,这很好,但是您只能使用strtok一次\n来解析fileOutput中的每一行,而不是再次嵌套来解析“:”。否则,由于Strutk通过在找到的定界符中插入“0”来修改字符串,您将在行中写入NUL终止字符,这将导致外部Strutk考虑下一次迭代结束的字符串。
相反,在每一行上使用一个单独的指针和strhr行“:”来定位该行的第一个“:”,然后使用指向行开头的指针和指针定位“:”。例如,如果您有一个函数来检查文件中是否包含用户名,成功时返回0,失败时返回1,则可以执行以下操作:
...
for (char *line = strtok(fileOutput,"\n"); line; line = strtok (NULL, "\n"))
{
char *p = strchr (line, ':'); /* find first ':' */
if (!p) /* if not found, bail */
break;
if (strncmp (userName, line, p - line) == 0) { /* check name */
printf ("found user: %s hash: %s\n", userName, p+1);
return 0;
}
}
fputs ("user not found.\n", stdout);
return 1;
这可能是您可以采取的更简单的方法之一。Strtok修改其输入字符串,这使得无法在嵌套模式下调用它,内部循环工作会破坏外部Strtok的工作,使其无法继续
显然,在您的问题中使用strtok是不够的,还有另一个原因:如果您试图使用它来解析/etc/passwd文件或我们今天处理的类似格式的文件,您将在空字段中遇到麻烦。如果有一个连续两个:字符的空字段,strtok将跳过这两个字符,完全跳过未检测到的空字段strtok是一个旧的遗留函数,编写此函数是为了处理bourne shell中用于分隔参数的三个字符。对于/etc/passwd,您需要处理可能为空的字段,这使得无法使用strtok解析它们
您可以轻松地使用strchr以非跳过的方式搜索:of/etc/passwd,只需编写类似于可以将其封装在函数中的内容:
char *not_strtok_but_almost(char *s, char *delim)
{
static char *p = NULL; /* this makes the function non-reentrant, like strtok() */
char *saved = NULL;
if (s) {
p = s;
while (strchr(delim, *p)) /* while *p is in the delimiters, skip */
p++;
/* *p is not more in the delimiters. save as return value */
saved = p;
}
/* search for delimiters after value */
while (*p && !strchr(delim, *p)) /* while *p not null, and not in the delimiter set */
p++;
/* *p is at the end of the string or p points to one of the delimiters */
*p = '\0';
return saved;
}
这个函数有strtok3的所有问题,但是您可以使用它来处理它的不可重入性,并且它修改源字符串,使它不能嵌套在多个循环中,因为它不会在一次跳过所有分隔符,而是在找到第一个分隔符后停止
为了解决嵌套问题,您可以采用另一种方式,假设您在/etc/group文件中有多个标识符,它们之间用空格分隔,这应该是需要的。但是,由于名称字段是最后一个字段,您不需要在第一个循环中再次调用strtok,而是要得到一个NULL。您可以以级别优先而不是深度优先的方式处理文件。首先查找第一级中的所有字段,然后逐字段读取可能使用不同分隔符的子字段
由于所有这些修改都是在同一个字符串中完成的,所以在使用之前不需要为每个字符串分配缓冲区并对字符串进行strdup。。。这项工作可以在同一个文件中完成,如果需要存储不同的子字段,可以在开始时对字符串进行strdup
如果您对此有疑问,请发表任何意见。请小心,因为我没有测试上面的例程,它可能有一个bugStrtok修改其输入字符串,这使得无法在嵌套模式下调用它,内部循环工作会破坏外部Strtok的工作,使其无法继续
显然,在您的问题中使用strtok是不够的,还有另一个原因:如果您试图使用它来解析/etc/passwd文件或我们今天处理的类似格式的文件,您将在空字段中遇到麻烦。如果有一个连续两个:字符的空字段,strtok将跳过这两个字符,完全跳过未检测到的空字段strtok是一个旧的遗留函数,编写此函数是为了处理bourne shell中用于分隔参数的三个字符。对于/etc/passwd,您需要处理可能为空的字段,这使得无法使用strtok
来解析它们
您可以轻松地使用strchr以非跳过的方式搜索:of/etc/passwd,只需编写类似于可以将其封装在函数中的内容:
char *not_strtok_but_almost(char *s, char *delim)
{
static char *p = NULL; /* this makes the function non-reentrant, like strtok() */
char *saved = NULL;
if (s) {
p = s;
while (strchr(delim, *p)) /* while *p is in the delimiters, skip */
p++;
/* *p is not more in the delimiters. save as return value */
saved = p;
}
/* search for delimiters after value */
while (*p && !strchr(delim, *p)) /* while *p not null, and not in the delimiter set */
p++;
/* *p is at the end of the string or p points to one of the delimiters */
*p = '\0';
return saved;
}
这个函数有strtok3的所有问题,但是您可以使用它来处理它的不可重入性,并且它修改源字符串,使它不能嵌套在多个循环中,因为它不会在一次跳过所有分隔符,而是在找到第一个分隔符后停止
为了解决嵌套问题,您可以采用另一种方式,假设您在/etc/group文件中有多个标识符,它们之间用空格分隔,这应该是需要的。但是,由于名称字段是最后一个字段,您不需要在第一个循环中再次调用strtok,而是要得到一个NULL。您可以以级别优先而不是深度优先的方式处理文件。首先查找第一级中的所有字段,然后逐字段读取可能使用不同分隔符的子字段
由于所有这些修改都是在同一个字符串中完成的,所以在使用之前不需要为每个字符串分配缓冲区并对字符串进行strdup。。。这项工作可以在同一个文件中完成,如果需要存储不同的子字段,可以在开始时对字符串进行strdup
如果您对此有疑问,请发表任何意见。请小心,因为我没有测试上述例程,它可能有错误@KagurazakaKotori how?char*strtok_rchar*str,const char*delim,char**saveptr;使用strtok,您可以按“\n”解析行,然后再按;。当你拜访时;插入“\0”修改行,以便下次调用外部strtok时遇到“\0”时失败。使用重新进入版本。。。或者更好-使用非修改方法提取令牌,如一对指针和/或对strcspn和strspn或strpbrk、strchr等的调用解析所需的任何东西——为什么不在fgets中使用面向行的读取——都有点令人困惑……@David C.Rankin我不能使用fgets。我的阅读能力有限。。我如何解决这个问题?:/你能写出代码片段吗?@KagurazakaKotori how?char*strtok_rchar*str,const char*delim,char**saveptr;使用strtok,您可以按“\n”解析行,然后再按;。当你拜访时;插入“\0”修改行,以便下次调用外部strtok时遇到“\0”时失败。使用重新进入版本。。。或者更好-使用非修改方法提取令牌,如一对指针和/或对strcspn和strspn或strpbrk、strchr等的调用解析所需的任何东西——为什么不在fgets中使用面向行的读取——都有点令人困惑……@David C.Rankin我不能使用fgets。我的阅读能力有限。。我如何解决这个问题?:/你能写出代码片段吗?C.Rankin实际上我想要单独的值。Salt值和加密值。在你的回答中,它给了我一个单一的价值观——没问题。不客气。我只是在做另一件事。很高兴你把它整理好了。这是一种巧妙的方法。C.兰金实际上我想要单独的值。Salt值和加密值。在你的回答中,它给了我一个单一的价值观——没问题。不客气。我只是在做另一件事。很高兴你把它整理好了。这是一种巧妙的方式。