-1

I basically want to take a single line of input from stdin, grab 'strings' (an array of char's separated by a space), and then place each 'string' into an array of char[]. I've already tried using fgets, getline, and strtok; I don't want to use outside libraries or anything that is unavailable to Unix. I've seen a lot of similar questions on here so I'm sorry if this has already been answered, but I can't find a straightforward, cut and dry way to do this.

Thanks, and I apologize if I've termed anything incorrectly, I am very much not used to C.

EDIT: Okay, so here's what I've tried most recently:

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

int main() {
const char str[80] = "This is a | test";
const char s[2] = " ";
char *token;
char arguments[10][50];
int i = 0;

/* get the first token */
token = strtok(str, s);

/* walk through other tokens */
while( token != NULL )
{
  printf( " %s\n", token );
  arguments[i] = token;
  i++;
  token = strtok(NULL, s);
}

return(0);
}

EDIT: Thanks Paul, for anyone with the same question as I had, I got it to work by changing the line that says:

arguments[i] = token;

to:

strcpy(arguments[i], token);
  • 1
    Show what you're tried and how it's failed. A combination of `fgets()` and either `strtok()` or manual parsing is the normal way to do it, so if that's not working for you, then making it work for you is what you should do instead of looking for another way. – Crowman Sep 27 '14 at 18:27
  • `arguments[i] = token;` - you can't assign to arrays like that, you need to use `strcpy()`. `strtok()` modifies the string you pass to it, also, so you shouldn't make it `const`. This is all very basic, I think you need to spend a little more time learning C before asking questions about it on Stack Overflow. – Crowman Sep 27 '14 at 18:44
  • I think that the `argv` (in `int main(int argc, char * * argv);`) might be just what you need. Note that it is generally better style to design programs to obtain their arguments as command line arguments than to query the user interactively. Unfortunately, the latter is what most books and classes teach you. – 5gon12eder Sep 27 '14 at 18:52

1 Answers1

1

Split it into steps:

  • First, get a whole line of input using getline (a GNU extension; if it is not present, implement your own as a loop around fgets)

  • Next, perform newline canonicalization. Some applications will want the trailing newline always; others will want it never; getline will give it to you sometimes, so you need to force it one way or another (I recommend removal, since it's just a conditional assignment of a NUL byte, rather than a conditional realloc). Note that if you want to DTRT with carriage returns (to handle windows and classic mac files - which yes, still exist in the wild - you'll need to write your own function instead of getline)

  • At this point, you have ownership of a malloced string containing the whole line. I'm going to assume you want to have access to all of the arguments at once (in an array) rather than one at a time, so you'll need to allocate a big enough array; it is often convenient to do this by performing an early pass over the input just to count the number of separators (if you do this in a higher-level language that provides arrays as a class, you usually push incrementally instead). DO NOT USE A FIXED SIZE ARRAY LIKE MOST EXAMPLES. IT IS NEVER ACCEPTABLE TO LEARN A BAD HABIT THAT WILL CAUSE SECURITY BUGS IN THE REAL WORLD. Alternatively, if you know exactly how many tokens to expect according to the code, you can allocate exactly that many and error out if there are too many or too few.

  • Now iterate over the string. I tend to avoid strtok because it has numerous problems, some of which are not solved with strtok_r, so roll my own search either using direct iteration (useful if I need to do additional preprocessing such as backslash or quote handling - which can be done in-place, without allocation, remember) or using strchr (for a single character; see also strchrnul for a more convenient GNU extension) or strpbrk/strspn/strcspn (to search for any of a set of characters, e.g. space or tab (note that baskslash/quote handling can be done with this too, and may be more efficient than a manual loop. But for the kind of thing you usually spend time parsing, readability is far more important than efficiency, so what matters is what you can make readable))

  • For each word, store a NUL on top of the delimiter and store the pointer to the start of the word into the array, then skip over the delimiter to start the next word. If you hit the end of the input string instead of the delimiter, break.

  • Finally, write a set of test cases. Some important cases that come to mind are: empty line, line with only spaces, line with spaces at beginning/end, line with runs of adjacent spaces, line with only one word, line with many words; repeat all of the above with each delimiter and with/without each form of trailing newline. You'll probably discover some bugs in your code, so go back and fix it. (Some advocate for writing the test cases first and writing code only as necessary to make the tests pass, but I imagine you don't have a grasp on what sort of testing helps find bugs, but you'll gain it as you write the bugs. If you want an interesting experiment, try deleting your main source file after your tests are written, and then see if your testsuite is enough to force you to rewrite it correctly. If not, you need more tests.)

o11c
  • 15,265
  • 4
  • 50
  • 75