我正在尝试将一个字符和两个字符串保存到变量中。 我使用sscanf来读取以下形式的字符串:
N "OldName" "NewName"
我想要的是:char character =' N' ,char * old_name =" OldName" ,char * new_name =" NewName" 。
这就是我尝试这样做的方式:
sscanf(mystring,"%c %s %s",&character,old_name,new_name);
printf("%c %s %s",character,old_name,new_name);
问题是,我的问题在没有任何输出的情况下停止工作。 (我也想忽略引号并仅保存其内容)
答案 0 :(得分:3)
当你这样做时
char* new_name = "NewName";
使指针new_name
指向包含常量字符串文字的只读字符串数组。该数组恰好包含8个字符(字符串的字母加上终结符)。
首先,将该指针用作scanf
的目标将导致scanf
写入只读数组,从而导致未定义的行为。如果你给一个超过7个字符的字符串,那么scanf
也会尝试写出越界,再次导致未定义的行为。
简单的解决方案是使用实际数组,而不是指针,并告诉scanf
读取的数据不能超过数组。像这样:
char old_name[64]; // Space for 63 characters plus string terminator
char new_name[64];
sscanf(mystring,"%c %63s %63s",&character,old_name,new_name);
要跳过引号,您有两个选择:使用指针和指针算法跳过前导引号,然后将字符串终止符设置在最后一个引号的位置为"删除"它。另一个解决方案是move字符串覆盖引号,然后按照上一个解决方案删除最后一个引号。
或者您可以依赖scanf
(and family)的有限模式匹配功能:
sscanf(mystring,"%c \"%63s\" \"%63s\"",&character,old_name,new_name);
请注意,上述sscanf
调用将起作用 iff 字符串实际包含引号。
第二个注意事项:正如Cool Guy的评论中所述,上述因为scanf
贪婪而赢得了实际工作。它将一直读到文件/字符串的结尾或空格,因此它实际上不会在结束双引号处停止读取。使用scanf
和家庭的唯一工作解决方案是下面的解决方案。
另请注意scanf
和家人在使用"%s"
阅读字符串时停止阅读空格,因此如果字符串为"New Name"
则会赢得&也可以工作。如果是这种情况,那么您需要手动解析字符串,或使用奇怪的"%["
格式,如
sscanf(mystring,"%c \"%63[^\"]\" \"%63[^\"]\"",&character,old_name,new_name);
答案 1 :(得分:2)
您必须为字符串分配空间,例如:
char* old_name = malloc(128);
char* new_name = malloc(128);
或使用数组
char old_name[128] = {0};
char new_name[128] = {0};
如果是malloc,您还必须在程序结束前释放空间。
free(old_name);
free(new_name);
答案 2 :(得分:1)
更新:...
其他答案提供了创建内存的好方法以及如何将示例输入读入缓冲区。还有两个可能有用的项目:
1)您表示您也想忽略引号 2)先阅读&与空格分隔时的姓氏。 (示例输入不是)
正如@Joachim所指出的,因为scanf
和家人停止使用%s
格式说明符扫描空格,所以包含诸如“firstname lastname”之类的空格的名称将无法完全读取。有几种方法可以解决这个问题。这是两个:
方法1 :将您的输入标记为。
Tokenizing 一个字符串将其分成由分隔符分隔的部分。例如,您的字符串输入示例由至少3个可用分隔符分隔:空格:" "
,双引号:"
和换行符:\n
个字符。 fgets()
和strtok()
可用于读取所需内容,同时剥离任何不需要的字符。如果正确完成,此方法可以保留内容(甚至空格),同时删除"
等分隔符。以下概念的一个非常简单的例子包括以下步骤:
1)将stdin
读入带fgets(...)
的行缓冲区
2)使用strtok(...)
解析输入。
注意:这是一个说明性的,简单的实现,按顺序编码以匹配您的输入示例(带空格),并且不包括通常包含的错误检查/处理。
int main(void)
{
char line[128];
char delim[] = {"\n\""};//parse using only newline and double quote
char *tok;
char letter;
char old_name[64]; // Space for 63 characters plus string terminator
char new_name[64];
fgets(line, 128, stdin);
tok = strtok(line, delim); //consume 1st " and get token 1
if(tok) letter = tok[0]; //assign letter
tok = strtok(NULL, delim); //consume 2nd " and get token 2
if(tok) strcpy(old_name, tok); //copy tok to old name
tok = strtok(NULL, delim); //consume 3rd " throw away token 3
tok = strtok(NULL, delim); //consume 4th " and get token 4
if(tok) strcpy(new_name, tok); //copy tok to new name
printf("%c %s %s\n", letter, old_name, new_name);
return 0;
}
注意:如上所述,此示例(与大多数strtok(...)
实现一样)需要非常狭义的输入。在这种情况下,输入不得超过127个字符,由单个字符后跟空格组成,然后是双引号字符串,后跟更多空格,然后是另一个双引号字符串,如您的示例所定义:
N "OldName" "NewName"
以下输入也适用于上述示例:
N "old name" "new name"
N "old name" "new name"
还请注意此示例, some 认为strtok()
已被破坏,而其他 suggest avoiding 则使用它。我建议谨慎使用它,并且只在单线程应用程序中使用它。
方法2 :走绳子。
C字符串只是以{NULL}字符结尾的char
数组。通过有选择地将某些字符复制到另一个字符串中,同时绕过您不想要的字符(例如"
),您可以有效地从输入中删除不需要的字符。以下是一个示例函数:
char * strip_ch(char *str, char ch)
{
char *from, *to;
char *dup = strdup(str);//make a copy of input
if(dup)
{
from = to = dup;//set working pointers equal to pointer to input
for (from; *from != '\0'; from++)//walk through input string
{
*to = *from;//set destination pointer to original pointer
if (*to != ch) to++;//test - increment only if not char to strip
//otherwise, leave it so next char will replace
}
*to = '\0';//replace the NULL terminator
strcpy(str, dup);
free(dup);
}
return str;
}
示例用例:
int main(void)
{
char line[128] = {"start"};
while(strstr(line, "quit") == NULL)
{
printf("Enter string (\"quit\" to leave) and hit <ENTER>:");
fgets(line, 128, stdin);
sprintf(line, "%s\n", strip_ch(line, '"'));
printf("%s", line);
}
return 0;
}