Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用sscanf读取字符串_C_Input_Scanf - Fatal编程技术网

使用sscanf读取字符串

使用sscanf读取字符串,c,input,scanf,C,Input,Scanf,我试图将一个字符和两个字符串保存到变量中。 我使用sscanf读取以下形式的字符串: N "OldName" "NewName" 我想要的:charcharacter='N',char*old\u name=“OldName”,char*new\u name=“NewName” 这就是我试图做到的: sscanf(mystring,"%c %s %s",&character,old_name,new_name); printf("%c %s %s",character,old_name

我试图将一个字符和两个字符串保存到变量中。 我使用sscanf读取以下形式的字符串:

N "OldName" "NewName"
我想要的:charcharacter='N',char*old\u name=“OldName”,char*new\u name=“NewName”

这就是我试图做到的:

sscanf(mystring,"%c %s %s",&character,old_name,new_name);
printf("%c %s %s",character,old_name,new_name);
问题是,我的问题在没有任何输出的情况下停止工作。
(我也想忽略引号,只保存其内容)

您必须为字符串分配空间,例如:

char* old_name = malloc(128); 
char* new_name = malloc(128);
或者使用数组

char old_name[128] = {0};
char new_name[128] = {0};
在这种情况下,您还必须在程序结束前释放空间

free(old_name);
free(new_name);
当你这样做的时候

char* new_name = "NewName";
使指针
new\u 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);

要跳过引号,您有两种选择:使用指针和指针算法跳过前导引号,然后在最后一个引号处设置字符串终止符以“删除”它。另一种解决方案是使用字符串覆盖前导引号,然后按照上一种解决方案删除最后一个引号

或者,您可以依赖以下有限的模式匹配功能:

请注意,上面的
sscanf
调用将在iff时起作用,字符串实际上包含引号

第二个注意事项:正如Cool Guy在评论中所说的,上述方法实际上不起作用,因为
scanf
是贪婪的。它将一直读取到文件/字符串的结尾或空白处,因此实际上不会在结束双引号处停止读取。使用
scanf
和系列的唯一有效解决方案如下所示

还要注意的是,
scanf
和family在使用
“%s”
读取字符串时会停止在空白处读取,因此如果字符串是
“新名称”
,则它也不会工作。如果是这种情况,则需要手动解析字符串,或者使用奇数
“%[”
格式,类似

sscanf(mystring,"%c \"%63[^\"]\" \"%63[^\"]\"",&character,old_name,new_name);
更新:

其他答案提供了创建内存的好方法,以及如何将示例输入读入缓冲区。还有两项可能会有所帮助:

1) 您表示希望忽略引号。
2) 用空格分隔时读取名字和姓氏。(示例输入不是)

正如@Joachim所指出的,由于
scanf
和family停止使用
%s
格式说明符扫描某个空间,因此包含诸如“firstname lastname”等空间的名称将无法完全读入。有几种方法可以解决此问题。以下是两种:

方法1:标记您的输入。
字符串将其拆分为由分隔符分隔的部分。例如,您的字符串输入示例至少由3个可用分隔符分隔:空格:
、双引号:
、换行符:
\n
字符。
fgets()
strtok()
可用于读入所需内容,同时去除任何不需要的字符。如果操作正确,此方法可以保留内容(甚至是空格),同时删除分隔符,如
。以下概念的一个非常简单的示例包括以下步骤:
1) 使用
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"

也注意这个例子,考虑<代码> Stotok()/Case>断开,而其他的则使用它。我建议谨慎使用它,只在单线程应用程序中使用。

方法2:移动字符串。
C字符串只是一个以空字符结尾的
字符数组。通过有选择地将某些字符复制到另一个字符串中,同时绕过您不需要的字符(例如
),可以有效地从输入中删除不需要的字符。下面是一个示例函数,可实现此目的:

 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;   
}
int main(无效)
{
字符行[128]={“开始”};
while(strstr(第行,“退出”)==NULL)
{
printf(“输入字符串(\'quit\'离开)并点击:”;
fgets(第128行,标准DIN);
sprintf(行,“%s\n”,条带(行,”);
printf(“%s”,第行);
}
返回0;
}

您是如何声明
旧名称的
新名称的
?我打赌
char*old\u name;
char*old\u name,char*new\u name;您必须为字符串分配空间,至少使用mallocy您可以使用格式字符串
“%c\”%[^\“]\“%[^\”]\”
@SouravGhosh,这不行。请参阅。展开↑ 方法是正确的。工作得很有魅力!有没有办法阅读引号的内容,而不包括它们?我的意思是
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;   
}