Go Down

Topic: indexOf does not work with a string value? (Read 1 time) previous topic - next topic

spatula

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.

westfw

Quote
Quote
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++ <string> method. ANy other options?

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

Code: [Select]
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));
     :


Quote
...regular expression...

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

Nick Gammon


Quote
...regular expression...

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


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

http://en.wikipedia.org/wiki/Parsing_expression_grammar
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

mistergreen

#18
Feb 16, 2013, 07:50 pm Last Edit: Feb 16, 2013, 07:57 pm by mistergreen Reason: 1

Quote
Quote
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++ <string> method. ANy other options?

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

Code: [Select]
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));
    :


Quote
...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.

Code: [Select]
  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 );


spatula

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

Code: [Select]

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


and this will locate parameter names and values
Code: [Select]


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.

zoomkat

#20
Feb 16, 2013, 11:14 pm Last Edit: Feb 16, 2013, 11:56 pm by zoomkat Reason: 1
Simple server code that uses indexOf and the memory saving F() macro for the static strings.

Code: [Select]

//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="";

       }
     }
   }
 }
}

Consider the daffodil. And while you're doing that, I'll be over here, looking through your stuff.   8)

westfw

Quote
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...
Code: [Select]
   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.)

Nick Gammon

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.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Go Up