如何在C语言中从字符串中提取子字符串?

10

我尝试使用strncmp,但它只在给定要提取的特定字节数时起作用。

char line[256] = This "is" an example. //I want to extract "is"
char line[256] = This is "also" an example. // I want to extract "also"
char line[256] = This is the final "example".  // I want to extract "example"
char substring[256]
如何提取位于 "" 之间的所有元素,并将其存储到变量 substring 中?

将字符串进行分词,使用 " 作为分隔符。取第二个结果。请参阅 strtok 的文档。这就是你所需要的... - Floris
5个回答

15
注意:我在意识到代码会出现问题时编辑了这个答案,因为strtok不喜欢在const char*变量上操作。这更多是我编写示例的方式造成的副作用,而不是底层原则的问题,但显然它值得双倍下投票。所以我修复了它。

以下代码有效(在Mac OS 10.7上使用gcc测试通过):
#include <stdio.h>
#include <string.h>

int main(void) {
const char* lineConst = "This \"is\" an example"; // the "input string"
char line[256];  // where we will put a copy of the input
char *subString; // the "result"

strcpy(line, lineConst);

subString = strtok(line,"\""); // find the first double quote
subString=strtok(NULL,"\"");   // find the second double quote

printf("the thing in between quotes is '%s'\n", subString);
}

这是它的工作原理: strtok 寻找“分隔符”(第二个参数)- 在这种情况下,是第一个 "。在内部,它知道“已经到哪里了”,如果您再次使用 NULL 作为第一个参数(而不是 char*),它将从那里重新开始。因此,在第二次调用中,它返回“第一个和第二个双引号之间的字符串”。 这就是您想要的。

警告: strtok 通常会用 '\0' 替换分隔符,因为它“吃掉”输入。因此,您必须指望这种方法修改您的输入字符串。如果不能接受这一点,您必须首先制作本地副本。本质上,当我将字符串常量复制到变量时,我就那样做了。如果您打算在函数内封装它,则必须考虑返回值在函数返回后仍然有效...因为 strtok 返回指向字符串内正确位置的指针,它不会复制标记。传递指向您想要结果结束的空间的指针,并在函数内创建该空间(具有正确的大小),然后将结果复制到其中,将是正确的操作。所有这些都非常微妙。如果不清楚,请告诉我!


有人可以解释一下我的解决方案有什么不受欢迎的地方吗? - Floris
比上面那个代码干净多了。谢谢。我最初没有接受它,因为它无法编译。我认为你不需要const char*然后再用strcpy。我正在寻找一个简单的函数调用,但是找不到。在数据结构课程中,运行时非常重要。谢谢! - ShadyBears
很高兴你现在喜欢它了。复制的原因是const char*可能无法修改,但这就是strtok所做的-请参见例如https://dev59.com/72ox5IYBdhLWcg3wIQ_3 - Floris
请注意,在结尾处还有重要的考虑事项,涉及到输入字符串、变量的持久性等。 - Floris
1
我根据您的答案进行了适应,以便我可以获取等号后面的所有内容... 我通过在第一个 strtok 中指定我的分隔符 "=",然后在第二个 strtok 中指定 "\0" 来获取行的其余部分。谢谢。 - Vince K
显示剩余2条评论

2

如果你想在没有库支持的情况下实现它...

void extract_between_quotes(char* s, char* dest)
{
   int in_quotes = 0;
   *dest = 0;
   while(*s != 0)
   {
      if(in_quotes)
      {
         if(*s == '"') return;
         dest[0]=*s;
         dest[1]=0;
         dest++;
      }
      else if(*s == '"') in_quotes=1;
      s++;
   }
}

然后调用它

extract_between_quotes(line, substring);


1
这是一种冗长的方法:假设要提取的字符串将用引号括起来。(已根据下面评论中kieth的错误检查建议进行修正)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){

    char input[100];
    char extract[100];
    int i=0,j=0,k=0,endFlag=0;

    printf("Input string: ");
    fgets(input,sizeof(input),stdin);
    input[strlen(input)-1] = '\0';

    for(i=0;i<strlen(input);i++){
        if(input[i] == '"'){

                j =i+1;
                while(input[j]!='"'){
                     if(input[j] == '\0'){
                         endFlag++;
                         break;
                     }
                     extract[k] = input[j];
                     k++;
                     j++;
                }
        }
    }
    extract[k] = '\0';

    if(endFlag==1){
        printf("1.Your code only had one quotation mark.\n");
        printf("2.So the code extracted everything after that quotation mark\n");
        printf("3.To make sure buffer overflow doesn't happen in this case:\n");
        printf("4.Modify the extract buffer size to be the same as input buffer size\n");

        printf("\nextracted string: %s\n",extract);
    }else{ 
       printf("Extract = %s\n",extract);
    }

    return 0;
}

输出(1):

$ ./test
Input string: extract "this" from this string
Extract = this

输出(2):
$ ./test
Input string: Another example to extract "this gibberish" from this string
Extract = this gibberish

输出(3):(Kieth建议进行错误检查) $ ./test
Input string: are you "happy now Kieth ?
1.Your code only had one quotation mark.
2.So the code extracted everything after that quotation mark
3.To make sure buffer overflow doesn't happen in this case:
4.Modify the extract buffer size to be the same as input buffer size

extracted string: happy now Kieth ?

--------------------------------------------------------------------------------------------------------------------------------

尽管没有被要求,但以下代码从输入字符串中提取多个用引号括起来的单词:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){

    char input[100];
    char extract[50];
    int i=0,j=0,k=0,endFlag=0;

    printf("Input string: ");
    fgets(input,sizeof(input),stdin);
    input[strlen(input)-1] = '\0';

    for(i=0;i<strlen(input);i++){
        if(input[i] == '"'){
            if(endFlag==0){
                j =i+1;
                while(input[j]!='"'){
                     extract[k] = input[j];
                     k++;
                     j++;
                }
                endFlag = 1;
            }else{
               endFlag =0;
            }

            //break;
        }
    }

    extract[k] = '\0';

    printf("Extract = %s\n",extract);

    return 0;
}

输出:

$ ./test
Input string: extract "multiple" words "from" this "string"
Extract = multiplefromstring

这个答案预设你已经对字符串的所有内容都了如指掌,除了引号中的那一部分。这很不可能,尤其是在给出三个例子的情况下。 - Floris
1
@KeithNicholas 楼主没有要求错误检查..我提供的解决方案正是楼主所要求的。剩下的错误检查工作就留给楼主自行处理,除非楼主有要求。 - sukhvir
在处理字符串时,无论操作要求还是是否知道要求,您都应始终尊重0终止。这只是零终止字符串的基本概念。 - Keith Nicholas
@KeithNicholas - 如果您不喜欢笨重的代码,那么也许您会喜欢我发布的解决方案(最初由于一个错误而遭到一些人的投票反对,但我相信我已经修复了这个问题)?只需两次调用 strtok - Floris
是的,strtok可以,我实际上期望一个干净的strtok解决方案被完成和接受,这就是为什么我只做了一个非基于库的解决方案。 - Keith Nicholas
显示剩余3条评论

1
#include <string.h>
...        
substring[0] = '\0';
const char *start = strchr(line, '"') + 1;
strncat(substring, start, strcspn(start, "\""));

此处省略了范围和错误检查。避免使用strtok,因为它具有副作用。


0

你尝试过查看 strchr 函数吗?你应该能够调用该函数两次,以获取指向第一个和第二个 " 字符实例的指针,并使用 memcpy 和指针算术的组合来获得你想要的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接