使用sscanf读取字符串

时间:2016-04-21 12:21:41

标签: c input scanf

我正在尝试将一个字符和两个字符串保存到变量中。 我使用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);

问题是,我的问题在没有任何输出的情况下停止工作。 (我也想忽略引号并仅保存其内容)

3 个答案:

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