4

If I have a string such as the string that is the command

echo 'foobar'|cat

Is there a good way for me to get the text between the quotation marks ("foobar")? I read that it was possible to use scanf to do it in a file, is it also possible in-memory?

My attempt:

  char * concat2 = concat(cmd, token);
  printf("concat:%s\n", concat2);
  int res = scanf(in, " '%[^']'", concat2);
  printf("result:%s\n", in);
gsamaras
  • 71,951
  • 46
  • 188
  • 305
Niklas Rosencrantz
  • 25,640
  • 75
  • 229
  • 424

3 Answers3

5

Use strtok() once, to locate the first occurrence of delimiter you wish (' in your case), and then once more, to find the ending pair of it, like this:

#include <stdio.h>
#include <string.h>

int main(void) {
  const char* lineConst = "echo 'foobar'|cat"; // 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

  if(!subString)
    printf("Not found\n");
  else
    printf("the thing in between quotes is '%s'\n", subString);
  return 0;
}

Output:

the thing in between quotes is 'foobar'


I was based on this: How to extract a substring from a string in C?

Community
  • 1
  • 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • Would be worthy of a vote if it didn't have extraneous "\" characters inserted into both the original string and the strtok search tokens. They do nothing to help the issue and merely confuse it, I assert. Certainly, if the original string was `echo "foobar"|cat`, I agree in a second. But it's not. There's nothing that needs escaping. I'm actually rather curious of your motivation for adding them in this case. – enhzflep Apr 24 '16 at 19:19
  • 1
    @enhzflep thanks for that, I improved my answer, is it better now?:) I tested with double quotes at first (which require escaping) and was planning to remove the escaping for the single quotes, but then ameyCU's answer came up and I got caught up with that. – gsamaras Apr 24 '16 at 19:21
  • You're welcome. _I_ think it is, I am but one man though. You've already garnered my vote for it. (kidding-->) I've got a trophy somewhere that says "easily distracted",I get side-tracked every time I search for it.. :laughs: – enhzflep Apr 24 '16 at 19:25
  • @enhzflep haha, one man with your rep means a lot. :) Hope it helps the OP! – gsamaras Apr 24 '16 at 19:26
3

If your string is in this format -"echo 'foobar'|cat", sscanf can be used-

char a[20]={0};
char *s="echo 'foobar'|cat";
if(sscanf(s,"%*[^']'%[^']'",a)==1){
   // do something with a
} 
else{
 // handle this condition 
}

%*[^'] will read and discard a string until it encounter single quote ' , the second format specifier %[^'] will read string till ' and store it in a.

ameyCU
  • 16,489
  • 2
  • 26
  • 41
  • 1
    Nice answer! However, it does't set `a` to NULL if not found, or something. How about `char a[20] = {0};`? :) Moreover, in the case it will find what we are looking for, does `sscanf()` take care the `\0`? – gsamaras Apr 24 '16 at 19:16
  • 1
    @gsamaras Initialization would be good and also checking of `sscanf` should be done . – ameyCU Apr 24 '16 at 19:17
  • 2
    Exactly! Your answer is more elegant, but I will keep mine as an alternative. +1. – gsamaras Apr 24 '16 at 19:19
  • 1
    @gsamaras Yup , made the changes . :) – ameyCU Apr 24 '16 at 19:23
  • I like the solution, but what if the string begins with a *single-quote* (e.g. `char *s="'foobar'|cat";`) The original assignment-suppression fails (which causes fits with the remainder of the format-string). I've tried several alternative and cannot find one that works in both cases. Do you have any suggestions for a format string that would work regardless of whether the first character is a single-quote? – David C. Rankin Apr 24 '16 at 21:45
1

There are a large number of ways to approach the problem. From walking a pair of pointers down the string to locate the delimiters, and a large number of string functions provided in string.h. You can make use of character search functions such as strchr or string search functions like strpbrk, you can use tokenizing functions like strtok, etc...

Look over and learn from them all. Here is an implementation with strpbrk and a pointer difference. It is non-destructive, so you need not make a copy of the original string.

#include <stdio.h>
#include <string.h>

int main (void) {

    const char *line = "'foobar'|cat";
    const char *delim = "'";        /* delimiter, single quote */
    char *p, *ep;

    if (!(p = strpbrk (line, delim))) { /* find the first quote */
        fprintf (stderr, "error: delimiter not found.\n");
        return 1;
    }
    p++;                        /* advance to next char */
    ep = strpbrk (p, delim);    /* set end pointer to next delim */

    if (!p) {   /* validate end pointer */
        fprintf (stderr, "error: matching delimiters not found.\n");
        return 1;
    }

    char substr[ep - p + 1];        /* storage for substring */
    strncpy (substr, p, ep - p);    /* copy the substring */
    substr[ep - p] = 0;             /* nul-terminate */

    printf ("\n single-quoted string : %s\n\n", substr);

    return 0;
}

Example Use/Output

$ ./bin/substr

 single-quoted string : foobar

Without Using string.h

As mentioned above, you can also simply walk a pair of pointers down the string and locate your pairs of quotes in that manner as well. For completeness, here is an example finding multiple quoted strings within a single line:

#include <stdio.h>

int main (void) {

    const char *line = "'foobar'|cat'mousebar'sum";
    char delim = '\'';
    char *p = (char *)line, *sp = NULL, *ep = NULL;
    size_t i = 0;

    for (; *p; p++) {                /* for each char in line */
        if (!sp && *p == delim)             /* find 1st delim */
            sp = p, sp++;                   /* set start ptr  */
        else if (!ep && *p == delim)        /* find 2nd delim */
            ep = p;                         /* set end ptr    */
        if (sp && ep) {                     /* if both set    */
            char substr[ep - sp + 1];       /* declare substr */
            for (i = 0, p = sp; p < ep; p++)/* copy to substr */
                substr[i++] = *p;
            substr[ep - sp] = 0;            /* nul-terminate  */

            printf ("single-quoted string : %s\n", substr);
            sp = ep = NULL;
        }
    }

    return 0;
}

Example Use/Output

$ ./bin/substrp
single-quoted string : foobar
single-quoted string : mousebar

Look all the answers over and let us know if you have any questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85