indexOf does not work with a string value?

mistergreen:
thanks, that did it. I had no idea a double quote made a difference.

And thank goodness the arduino has it's own String methods. Using C string methods were a pain.

Hopefully you don't plan on doing anything that push's the limits of the microcontrollers already limited SRAM.

mistergreen:
And thank goodness the arduino has it's own String methods. Using C string methods were a pain.

The c-string equivalents need hardly any more code, and avoid you needing to use the Arduino String class and the memory-related bugs that come with it. Unless you have patched your Arduino installation to correct these bugs, the String class is currently not safe to use. Even if you do fix the bugs, storing variable length dynamic data in the heap is asking for trouble on such a memory-limited architecture.

Oh, I didn't know the String object was such a problem.

How would I do the above code with C?

using strtr ?

My problem with the C string is converting the pointers returned into char so I can store them in a variable or array.

mistergreen:
using strtr ?

You can use strstr() to locate a string within another string, and substr() to extract part of a string into another variable. In your example, two calls to strstr() to locate the start and end of the section of interest, and one call to substr() to extract it into another buffer, would achieve the same effect as your original code. The two implementations would be pretty similar in terms of source code length and complexity.

mistergreen:
And thank goodness the arduino has it's own String methods. Using C string methods were a pain.

Please note that, at present, the String library has bugs as discussed here and here.

In particular, the dynamic memory allocation used by the String class may fail and cause random crashes.

I recommend reworking your code to manage without String. Use C-style strings instead (strcpy, strcat, strcmp, etc.).

Alternatively, install the fix described here:
http://arduino.cc/forum/index.php/topic,145765

PeterH:

mistergreen:
using strtr ?

You can use strstr() to locate a string within another string, and substr() to extract part of a string into another variable. In your example, two calls to strstr() to locate the start and end of the section of interest, and one call to substr() to extract it into another buffer, would achieve the same effect as your original code. The two implementations would be pretty similar in terms of source code length and complexity.

substr() is a C++ method. ANy other options?

Maybe it's an overkill but you could use a regular expression to parse the string. Example:

#include <Regexp.h>

char * mystring = "GET /http://arduino/?page=1&time=20,40,30,2,14,2013&param2=54& HTTP/1.1";

void setup ()
{
  Serial.begin (115200);
  MatchState ms;
  ms.Target (mystring);  // what to search

  char result = ms.Match ("time=(%d+),(%d+),(%d+),(%d+),(%d+),(%d+)");
  if (result == REGEXP_MATCHED)
    {
    char buf [10];  // to hold results
    int num;
    for (int i = 0; i < 6; i++)
      {
      Serial.print ("Result ");
      Serial.print (i);
      Serial.print (" = ");
      if (ms.capture [i].len < sizeof buf)
        {
        num = atoi (ms.GetCapture (buf, i));
        Serial.println (num); 
        }
      else
        Serial.println ("(too long)");  
      }  // end for loop
    }  // end if matched
  else if (result == REGEXP_NOMATCH)
    {
    Serial.println ("Did not match.");
    }
  else
    {
    Serial.println ("Error in regular expression.");
    }
}  // end of setup

void loop () { }

Output:

Result 0 = 20
Result 1 = 40
Result 2 = 30
Result 3 = 2
Result 4 = 14
Result 5 = 2013

Library from: http://gammon.com.au/Arduino/Regexp.zip

Documentation: Gammon Forum : Programming : General : Lua regular expression library that does not need Lua

mistergreen:
substr() is a C++ method. Any other options?

The c equivalent of a string is an array of char, so when you know the start and end position of the "substring" within the larger one you can just copy the array elements into a new array. Or if you need to make a comparison you may use the 'n' variant (strncmp) which compares two strings up to a certain number of characters.

I very much like the solution based on regular expressions. Mine, however, is the opposite. I see you are trying to parse the parameters of a query string. Your problem is quite specific and a less generic solution may work better in your case. Consider a loop on each character of the string.

Start by locating the '?' sign, i.e. the beginning of the query string, then:

  1. save the current position (+1) into a variable (e.g., name_start). From that position, start looking for the first '=' sign. The array between name_start and the position of '=' is the parameter name.
  2. save the current position (+1) into a variable (e.g., value_start). From that position, start looking for the first '&' sign or space.
    The array between value_start and the position of '&' or space is the parameter value.
  3. If you found a space exit the loop, otherwise repeat from 1.

You could then make improvements to make it more robust, e.g.: manage the premature end of the query string, manage spaces or url-encoded characters, but this should give you a start.

mistergreen:
substr() is a C++ method. ANy other options?

You could use strncpy().

For those interested, I got it to work with this SORT OF.. I used Xcode to write this (it's not fun to test on the arduino).
I wish arduino uses objective C :slight_smile:

    char mystring[] = "GET /http://arduino/?page=1&time=20,40,30,2,14,2013&param2=54 HTTP/1.1";
    char temp1[40];
    char temp2[40];
    
    char *a = strstr(mystring, "time");
    strncpy(temp1, a, sizeof(temp1));
    
    char *b = strstr(temp1, "&");
    
    strncpy(temp2, a, b-temp1);
    
    printf("%s  :  %s ", temp1, temp2 );

output

time=20,40,30,2,14,2013&param2=54 HTTP/1
  :  time=20,40,30,2,14,2013

but if I change the keyword search to 'page', I get some pointer junk at the end of the string.
output

page=1&time=20,40,30,2,14,2013&param2=54
  :  page=1\231\333h

Any ideas? Somehow convert the 'b' pointer to an integer?

page=1&time=20,40,30,2,14,2013&param2=54 HTTP/1.1

is 49 characters long.

Warning:

No null-character is implicitly appended at the end of destination if source is longer than num (thus, in this case, destination may not be a null terminated C string).

In the "time" case you were lucky to copy the remainder of the original string, starting from position 'a' and terminated by a null char. Not so with "page". As a quick and dirty solution you can initialize temp2 with zeroes, or append a null char after the copy, but if you don't take into account the length of the query string and the position of your keyword into it you are going to waste memory.

You can use strstr() to locate a string within another string, and substr() to extract part of a string into another variable.

substr() is a C++ method. ANy other options?

substr() is pretty easy to implement. Something like:

void substr(char *source, uint8_t start, uint8_t end, char *dest, uint8_t maxlength)
{
  source += start;  // point to start of substring
  maxlength--;   // leave room for a null at the end.
  while ( (*dest++ = *source++) != 0) {
      if (--maxlength == 0)
         break;
   }
   *dest = 0;
}
//Call it like:
void myfunc () {
   char mysubstring[10];
   char netresponse[80] = ...;
   uint8_t start = strstr(netresponse, "&");
   uint8_t end = strstr(netresponse, "time");
     :
   substr(netresponse, start, end, mysubstring, sizeof(mysubstring));
     :

...regular expression...

Jeffrey Friedl's Blog » Source of the famous “Now you have two problems” quote :slight_smile:

westfw:

...regular expression...

Jeffrey Friedl's Blog » Source of the famous “Now you have two problems” quote :slight_smile:

Well there are always PEGs but I find I really have to concentrate when using them, something I try not to do. :wink:

westfw:

You can use strstr() to locate a string within another string, and substr() to extract part of a string into another variable.

substr() is a C++ method. ANy other options?

substr() is pretty easy to implement. Something like:

void substr(char *source, uint8_t start, uint8_t end, char *dest, uint8_t maxlength)

{
 source += start;  // point to start of substring
 maxlength--;   // leave room for a null at the end.
 while ( (*dest++ = *source++) != 0) {
     if (--maxlength == 0)
        break;
  }
  *dest = 0;
}
//Call it like:
void myfunc () {
  char mysubstring[10];
  char netresponse[80] = ...;
  uint8_t start = strstr(netresponse, "&");
  uint8_t end = strstr(netresponse, "time");
    :
  substr(netresponse, start, end, mysubstring, sizeof(mysubstring));
    :






> ...regular expression...


http://regex.info/blog/2006-09-15/247 :-)

thanks for the code but strstr() requires a char * (it's a pointer) not uint8_t.

So in the end use the arduino String object or do what spatula suggests, whichever takes up less memory.

   char mystring[] = "GET /http://arduino/?page=1&time=20,40,30,2,14,2013&param2=54 HTTP/1.1";
    char temp1[40] = {0};
    char temp2[30] = {0};
    
    char *a = strstr(mystring, "time");
    strncpy(temp1, a, sizeof(temp1));
    
    char *b = strstr(temp1, "&");
    
    strncpy(temp2, a, b-temp1);
    
    printf("%s  :  %s ", temp1, temp2 );

A quick example of the diy approach. This will locate the beginning of the query string:

char *find_start_of_query_string(char *s) {
  while (*s != '?' && *s != '\0') {
    s++;
  }
  return s;
}

and this will locate parameter names and values

void parse_query_parameters(char *s) {
  char *name_start_pos;
  char *value_start_pos;
  size_t name_length;
  size_t value_length;

  while(1) { // return only on '\0'

    name_start_pos = ++s;
    name_length = 0;
// look for the equals sign or end-of-string
    while (*s != '=' && *s != '\0') {
      name_length++;
      s++;
    }
    if (*s == '\0') {
      return;
    }

    value_start_pos = ++s;
    value_length = 0;
// look for ampersand, space or end-of-string 
    while(*s != '&' && *s != ' ' && *s != '\0') {
      value_length++;
      s++;
    } 
    if (value_length > 0) {
// assuming you have declared and initialized a time_value variable, e.g.
// char time_value[20] = {0};
      if (strncmp("time", name_start_pos, name_length)) {
        strncpy(time_value, value_start_pos, value_length);  
      }      
    }
    if (*s == '\0') {
      return;
    }
  }
}

A tangential issue is that you don't want people to mess around with your parameters (e.g., sending "time=999999,abc,-15" in the query string). So you will probably need to have some control in place before accepting the values.

Simple server code that uses indexOf and the memory saving F() macro for the static strings.

//zoomkat 5-1-12
//simple button GET for servo and pin 5
//for use with IDE 1.0
//open serial monitor to see what the arduino receives
//use ' instead of " in html
//address will look like http://192.168.1.102:84 when submited
//for use with W5100 based ethernet shields

#include <SPI.h>
#include <Ethernet.h>

#include <Servo.h> 
Servo myservo;  // create servo object to control a servo 

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 102 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port

String readString; 

//////////////////////

void setup(){

  pinMode(5, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();

  myservo.write(90); //set initial servo position if desired
  myservo.attach(7);  //the pin for the servo control
  //enable serial data print 
  Serial.begin(9600); 
  Serial.println("server servo/pin 5 test 1.0"); // so I can keep track of what is loaded
}

void loop(){
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string 
          readString += c; 
          //Serial.print(c);
        } 

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging 

          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println(F("<HTML>"));
          client.println(F("<HEAD>"));
          client.println(F("<TITLE>Arduino GET test page</TITLE>"));
          client.println(F("</HEAD>"));
          client.println(F("<BODY>"));

          client.println(F("<H1>Zoomkat's simple Arduino button</H1>"));
          
          client.println(F("<a href='/?on'>ON</a>")); 
          client.println(F("<a href='/?off'>OFF</a>")); 

          client.println(F("</BODY>"));
          client.println(F("</HTML>"));
 
          delay(1);
          //stopping client
          client.stop();

          ///////////////////// control arduino pin
          if(readString.indexOf("on") >0)//checks for on
          {
            myservo.write(40);
            digitalWrite(5, HIGH);    // set pin 4 high
            Serial.println("Led On");
          }
          if(readString.indexOf("off") >0)//checks for off
          {
            myservo.write(140);
            digitalWrite(5, LOW);    // set pin 4 low
            Serial.println("Led Off");
          }
          //clearing string for next read
          readString="";

        }
      }
    }
  }
}

but strstr() requires a char * (it's a pointer) not uint8_t.

Ah. I didn't notice that it wasn't a direct replacement for indexOf...

   char mysubstring[80];
   char netresponse[80] = ...;
   char *substringStart = strstr(netresponse, "&"); // find start
   strcpy(mysubstring, substringStart);  // copy all stuff past start delimiter.
   substringStart = strstr(mysubstring, "time");  // find end
   *substringStart = 0;  // terminate the substring.

(Note that the storage reserved for the substring now needs to be larger, since it will temporarily hold the whole "second half" of the original string.)

The regular expression library to which I alluded further up was designed so that the captures did not require any extra memory. Not, at least, unless you need to copy them somewhere.

However in a simple case like this, a state machine should be all that is required.