Go Down

Topic: string replace? (Read 1 time) previous topic - next topic

fyrebug

does it exist? I could only find TextString which showed
void replace(char thisChar, char thatChar) - Replaces all the occurrences thsChar with thatChar

but I can't get it to work..

it errors for me.
I tried inString.replace(char* "<data value=", "");

and it says

error: expected primary-expression before 'char


I need to replace or find stuff as I'm trying to parse some xml..

thanks. sorry if this is a dumb question. I've been at it all morning and afternoon...

YenTheFirst

#1
May 30, 2009, 06:39 pm Last Edit: May 30, 2009, 06:42 pm by YenTheFirst Reason: 1
2 things:
First off, it looks like that function doesn't take a string/char array, it takes a single character, and replaces only that.

TextString has String::contains(char* Substring), but no replacement functions, as far as I can tell.

Try something like this, maybe:
Code: [Select]

//using 'MyString' to represent whatever your string is
char * MyString = whateveryourstringis.

//we find every instance of the string to delete, and cut it out
char * string_to_delete = "<data value=";
int length_to_delete = strlen(string_to_delete);
char * start_of_deleted_data = strstr(MyString,string_to_delete);

//strstr will return a NULL pointer if the substring wasn't found
//otherwise, it will return a pointer to the start of the substring
while(start_of_deleted_data != NULL)
{
 //delete the substring, by shifting everything in the string over by length_to_delete
 for (int i=0; i<length_to_delete; i++)
 {
    start_of_deleted_data[i] = start_of_deleted_data[i+length_to_delete];
 }
 //search for the next instance of the string
 char * start_of_deleted_data = strstr(MyString,string_to_delete);
}


My code there is NOT complete. At least one error I can see right away is that it will go a bit crazy if your string to delete is at the very end or near the end of your big string.

The second problem, that any solution will have, is possible memory leaks. If you're allocating your strings dynamically (using malloc), which is what TextString seems to do, then when you shorten a string, you have to make sure to free() the freed area.

If you allocate statically (declaring a big buffer, and using that), you can just overwrite that whenever new data comes in.

Let me know if this is helpful at all.

edit: I realize my example code can be kind of thick if you're not familiar with pointers or string manipulation, so I'll try to find some useful links, and explain my code a bit better, if you want.
It's not stupid, it's advanced

mem

#2
May 30, 2009, 07:47 pm Last Edit: May 30, 2009, 07:48 pm by mem Reason: 1
Hi fyrebug, TextString has been replaced with  the String library. It has methods to find characters and strings and can replace characters. Do you really need to substitute strings in place rather than append found values to a new string?

Could you show an example (perhaps in pseudocode) of what you are trying to do.

fyrebug

Hi mem.

what I'm doing is reading xml lines from the web, and then try to get values from them.

example.
<data value="5"></data>

so I'd want to read the string and get back the number 5.
string replace seemed the easiest way. I could just remove the first and last part of the string and be left with only the number 5.

@yenthefirst I completely appreciate your example I think I'm just not comfortable enough in arduino yet to fully understand it.
I'm coming from a php/actionscript background.

mem

Here is the code I use to parse web pages with Arduino.  This fragment is part of an application that uses the Ethernet shield  and I have left out some of the Ethernet specific code but I hope it gives you an idea of how to do parsing.

This code uses two classes that I intend to  make into a library when I find the time. I hope the lack of documentation does not prevent its use as is.
Code: [Select]
class FindFieldClass {
 private:
    int  index;
    char *target;
    int  targetLen;
 public:
    void begin(char *target){ // set the target string to find
      this->target = target;    
      this->index = 0;
      this->targetLen = strlen(target);
    }
    boolean find(char c){  
       if( c == target[index])
       {
          if(++index >= this->targetLen)
             return true;
          else          
             return false;          
       }
       else{
          index = 0;
       }
    }  
};

class GetFieldClass {
 private:
    int  index;
    char startChar;
    char endChar;
 public:
    char  buffer[10]; // this buffer holds the field value if seek returns true

    void begin(char start, char end){ // set the start and end character that defines the field
      this->startChar = start;    
      this->endChar = end;
      index = -1; // index indicates position,  -1 means waiting for start char
    }
    boolean seek(char c){  // service function returns true when the field is found
        if( index == -1){
           if( c == this->startChar)
              index = 0;
        }
        else if( c == this->endChar){                      
           buffer[this->index] = 0; // terminate the string
           return true;
        }
        else{
          // its a field char so save in buffer
           buffer[index] = c;
           if( this->index < sizeof(buffer))
              this->index++ ;  
        }
        return false;
    }  
};

FindFieldClass findField;
GetFieldClass getField;


void setup()
{
 //  Ethernet.begin(mac, ip);
 Ethernet.begin(mac, ip, gateway, subnet);
 Serial.begin(9600);
 findField.begin("<data value="); // the field to search for

 delay(1000);

 Serial.println("connecting...");

 if (client.connect()) {
   Serial.println("connected");
   client.println("Your web page here"); // todo
   client.println("User-Agent: AVR ethernet");
   client.println();
 }
 else
 {
   Serial.println("connection failed");    
 }
}

void loop()
{

 if (client.available()) {
   char c = client.read();
   if (findField.find(c) == true)
   {
     getField.begin('\"', '\"');  // get the value between the quotes
     do
     {
         c = client.read();
     }
     while(getField.seek(c) == false);
     // after closing delimter has been found
    Serial.println(" !!! found field");
    Serial.println(getField.buffer);
    value = atof(getField.buffer);
    Serial.println(value);
   }
   ///---
   
   if (!client.connected())
   {
     Serial.println();
     Serial.println("disconnecting.");
     client.stop();
     delay( 30 * 1000); // wait 30 seconds  
   }
 }
}

YenTheFirst

You wouldn't really need to use replace, you just need to find the number and copy it into a new string.

mem's class does basically the same thing, but wraps it up in an easy-to-use library.

Here's another example, this one should be easier to understand.
Two things to remember when dealing with strings on arduino:
1) strings are basically just arrays of characters. I think PHP treats them a bit differently than regular arrays, but on arduino, they're just arrays, with the last element being '0' (to mark the end of the string)

2)pointers point to the beginnings of arrays, or a place in arrays.
Code: [Select]

1: char  MyString[] = whateveryourstringis;

2: char  string_to_find[] = "<data value=";
3: int length_of_find = strlen(string_to_find);

4: char * start_of_field = strstr(MyString,string_to_find);

5: char * start_of_data = start_of_field + length_of_find;

6: char CopiedData[80];

7: int index=0;
8: while(start_of_data[index] != ''')
9: {
10:  CopiedData[index] = start_of_data[index];
11:  index++;
12:}
13:CopiedData[index] = 0;


Explanation:
(lines 1-4) First, find where the data field is. We find the string "<data value='", and point to that position.
As an example:
<someotherdata><data value='5'><otherstuff>
                             ^
(line 5) Then, we move a pointer over, to where the data actually starts
<someotherdata><data value='5'><otherstuff>
                                                 ^
(line 6) We'll hold our copied data in CopiedData.
(lines 7-13)Then, we'll loop, and copy all that data to CopiedData.

(line 10) In every loop, we copy over a single character from the data to our CopiedData buffer,
(line 11) and then go to the next spot
(line 8) when we've reached the end of the data (the other ' ), we stop copying,
(line 13) and put a 0 at the end of the copied data to mark the end


Hopefully, this example is a bit easier to understand. :)
Another thing to note, though: no matter how you get the data out of the string, you're going to have string data, not a number. So, if you actually have to use the number for something, you'll have to convert from that string to a number.
It's not stupid, it's advanced

Go Up