2

In C++, I want to do

char buffer[1024] = "command insert file1 file2 ..."

and turn it into

*argv[0] = "command"
*argv[1] = "insert"
*argv[2] = "file1"

et cetera. Isn't there some simple way to do this, like split() or whatever? I can't use boost or vectors because the elements need to be c-strings for linux library functions (like execvp), and they need to compile on a server without boost or any extra libraries

I've seen examples online that use strtok, but the examples don't seem to store the values afterward, just print them. The other way seems to be some ridiculously complicated method that involves looping through and counting spaces and escape characters. Isn't there an easier way? This is driving me nuts.

EDIT: As a point of reference, this is what my main function looks like so far:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
#include <iostream>
using namespace std;

void runCommand(char **argv);
void splitIntoArgs(char *command, char **argv);

int main()
{

   char buffer[1024];
   char *argv[5];

   while (1)
   {
       // prompt user for command to run
       cout << "Enter command: ";
       cin >> buffer; //read the buffer
       cout << endl;
       splitIntoArgs(buffer, argv); //split the command into separate arguments
       if (strcmp(argv[0], "exit") == 0)
           return 0;
       runCommand(argv);
   }

}
qwerty26
  • 61
  • 2
  • 7
  • 1
    isn't arguments already just like how you want this, i don't think you need to work for it. See here for more details http://stackoverflow.com/questions/3024197/what-does-int-argc-char-argv-mean – Brij Raj Singh - MSFT Apr 17 '14 at 05:37
  • I'm making a rudimentary shell, that's why. It doesn't take these variables as arguments when the program starts. I'll edit my question to explain this – qwerty26 Apr 17 '14 at 05:39
  • 1
    `strtok` should work fine. Did you run into a problem storing the pointers? – Ben Voigt Apr 17 '14 at 05:40
  • BTW, assuming `char** argv`, then `*argv[0]` is a single character, not a string. Did you mean `argv[0] = "command";` ? – Ben Voigt Apr 17 '14 at 05:40
  • You could just replace the spaces by zéros and store the pointers to the starts of the strings in buffer. – Jabberwocky Apr 17 '14 at 05:40
  • @MichaelWalz: That's what `strtok` does. – Ben Voigt Apr 17 '14 at 05:41
  • @BenVoigt : right so strtok is the way to go after all. – Jabberwocky Apr 17 '14 at 05:41
  • 1
    Just because the examples you found of `strtok()` just show printing them, that doesn't mean it's the only thing you can do. You can store the values just as well. – Barmar Apr 17 '14 at 05:43
  • But what is the code to have strtok store a token? @Ben Voigt isn't it an array of character pointers, not characters, so it should be *argv[0]? – qwerty26 Apr 17 '14 at 05:44
  • 1
    @qwerty26: `argv` is an array of character pointers. `*argv` and `argv[0]`, which are the same thing, are a single character pointer. `*argv[0]` is a single character. You might be looking for `argv[0] = strtok( blah, blah );` – Ben Voigt Apr 17 '14 at 05:45
  • possible duplicate of [How to split a string in C++?](http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c) – juanchopanza Apr 17 '14 at 05:53

3 Answers3

1

You asked this as a c++ question, so why don't you use the power of c++ in the form of the standard library which gives you std::string and std::vector

#include <vector>
#include <string>

std::vector<std::string> split(const std::string& str){
    std::vector res;
    size_t old_position = 0;
    for(size_t position = 0; position = str.find(" ", position); position != std::npos){
        res.push_back(res.substr(position, position-old_position));
        old_position = position;
    }
}
niklasfi
  • 15,245
  • 7
  • 40
  • 54
  • I don't think I can use c++ strings, and perhaps not vectors since I am using the elements of the array in a c function. Perahps someone knows the execvp, execlp etc functions better and can say otherwise. – qwerty26 Apr 17 '14 at 15:31
0
// split string str on " ". The result array has count elements, 
// so make sure count > #of spaces + 1
char** split(char* str, int count){
   char** res = new char*[count];
   int res_i = 0;
   for(int i=0; str[i] != 0 && res_i < count; ++i){
       if(str[i] == ' '){
          res[res_i++] = i + 1;
          str[i] = 0;
       }
   }
   return res;
}

A couple of notes:

  • this code is not safe use. If for example the last character of "str" is a ' ', you would get a pointer to the null-terminator '\0'.
  • the standard reply to someone using char* for not specific reason in a c++ question still applies: use std::string!
  • split destroys the original string that was passed to it
niklasfi
  • 15,245
  • 7
  • 40
  • 54
0

Here is a strtok() version of splitting the command:

char** split(char *command, int* size) {
  char** ret;
  char* t;
  int i;
  for(i=0, t=strtok(command, " "); t!=NULL;++i ) {
    ret[i]=t;
    t = strtok(NULL, " ");
  }
  *size = i;
  return ret;
}

You can use it like this:

char** args;
int nArgs;
args = split(buffer, &nArgs);
// you can now eg. execvp("/bin/ls", args)
tpatja
  • 690
  • 4
  • 11