Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ Programming ➜ General ➜ Parsing a string into words

Parsing a string into words

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by Daniel Spain   (15 posts)  Bio
Date Fri 18 Nov 2011 03:41 AM (UTC)

Amended on Fri 18 Nov 2011 04:43 AM (UTC) by Nick Gammon

Message
ok keep in mind im 36, didnt learn this in school and am learning from the internet so if i do something completely absurd please bear with me.

in this code i am trying to parse a string into 2 things, a number of arguments, and a number of words.

for my sake a game command will never have more than 5 arguments such as "rob admin of 10 gold" or "give admin broadsword" and as small as "stats"

so i locked my arguments at 5 positions at 256 characters each.

so here we go.


static void ProcessGameCommands (string & sLine, Player * p)
{
     if(sLine.empty ())
     {
       Send(p, "Invalid command.\r\n");
     } 

     char input[256];
     strncpy(input,str(sLine),256);

     int argc = 1; 
     char argv[5][256];    

     int i;
     int pos = 0;

     for(i=0; i < (int) strlen(input); i++)
     {
       if(isspace(input[i])) argc++;
     }

     Send(p,"Number of arguments = %d.\r\n",argc);

     char * pch;
    
     pch = strtok (input,"'\40'");

     while (pch != NULL)
     {
      if( pos < 5)
      {
         strncpy(argv[pos],pch,256);
	 pos++;
         pch = strtok (NULL, "'\40");
      }
     }

     i=-1;
     while(++i<pos)
     Send(p,"Argument[%d] = %s\r\n",i,argv[i]);
}


i tested this with a small routine like this:


if(argc == 2 && !strcmp(argv[0],"exit") &&
                !strcmp(argv[1],"now"))
 Send(p, "I should exit the game now as requested.\r\n");


and it worked great, however i learned a long time ago that just because something works the first time does not always mean its the right way.
plus during my research i heard strtok was not good to use.

as always thanks.

[EDIT] Moderator: quoted forum codes.
Top

Posted by Nick Gammon   Australia  (23,173 posts)  Bio   Forum Administrator
Date Reply #1 on Fri 18 Nov 2011 04:48 AM (UTC)
Message
How about this?


inline string trim_right (const string & s, const string & t = SPACES)
  { 
  string d (s); 
  string::size_type i (d.find_last_not_of (t));
  if (i == string::npos)
    return "";
  else if ((i + 1) >= s.size ())
    return s;
  else
   return d.erase (i + 1) ; 
  }  // end of trim_right

inline string trim_left (const string & s, const string & t = SPACES) 
  { 
  string d (s); 
  return d.erase (0, s.find_first_not_of (t)) ; 
  }  // end of trim_left

inline string trim (const string & s, const string & t = SPACES)
  { 
  string d (s); 
  return trim_left (trim_right (d, t), t) ; 
  }  // end of trim


// split a line into the first word, and rest-of-the-line
string GetWord (string & s, 
                const string delim,
                const bool trim_spaces)
  {
    
  // find delimiter  
  string::size_type i (s.find (delim));

  // split into before and after delimiter
  string w (s.substr (0, i));

  // if no delimiter, remainder is empty
  if (i == string::npos)
    s.erase ();
  else
    // erase up to the delimiter
    s.erase (0, i + delim.size ());

  // trim spaces if required
  if (trim_spaces)
    {
    w = trim (w);
    s = trim (s);
    }

  // return first word in line
  return w;
  
  } // end of GetWord 


// To be symmetric, we assume an empty string (after trimming spaces)
// will give an empty vector.
// However, a non-empty string (with no delimiter) will give one item
// After that, you get an item per delimiter, plus 1.
// eg.  ""      => empty
//      "a"     => 1 item
//      "a,b"   => 2 items
//      "a,b,"  => 3 items (last one empty)

void StringToVector (const string s, 
                     vector<string> & v,
                     const string delim, 
                     const bool trim_spaces)
  {

  // start with initial string, trimmed of leading/trailing spaces if required
  string s1 (trim_spaces ? trim (s) : s);

  v.clear (); // ensure vector empty

  // no string? no elements
  if (s1.empty ())
    return;

  // add to vector while we have a delimiter
  while (!s1.empty () && s1.find (delim) != string::npos)
    v.push_back (GetWord (s1, delim, trim_spaces));

  // add final element
  v.push_back (s1);
  } // end of StringToVector 


StringToVector turns a string into a vector of things. Being a vector it can be any size (ie. any number of words). In your case the delimiter could be the space character.

This uses the Standard Template Library (STL). It is very useful indeed. You need a couple of includes to make it work.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


11,407 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.