Strings, Char, and Arrays

I've pulled my hair out long enough so time to ask for help. ;)

I have this;

int InStr( char c, char *str )
{
  char *cur = str;
  while(*cur)
    {
    if(*cur == c)
      return cur - str;
    ++cur;
    }
  return -1;
}

float test(char s, char e, char *str)
{
  int i = 0;
  int b = -1;
  //int t = sizeof(str);
  //Serial.println(t);
  char *ret;
  String r;
  boolean foundStart = false;
  while(*str)
    {
    if(str[i] == s)
      {
      foundStart = true;
      Serial.println(str[i]);
      i+=2;
      continue;
      }
    if(str[i] == e)
      {
      Serial.println(str[i]);
      break;
      }
    if(foundStart)
      {
      ret += str[i];
      //r += str[i];
      Serial.println(ret);
      Serial.println(str[i]); 
      }
    i++;
    }
 return atof(ret);  
}

void setup()
{
  Serial.begin(9600);
  
  char *str = "X:473.55Y:362.45Z:450.20E:1.11";
  float f_X = test('X', 'Y', str);    
  float f_Y = atof(str+InStr('Y', str)+2);
  float f_Z = atof(str+InStr('Z',str)+2);
  Serial.println( f_X );
  Serial.println(f_Y);
  Serial.println(f_Z);
}

void loop()
{
}

The first function InStr() I got from pYro_65 and seems to work good. But in an effort to try and understand what all VooDoo he used I tried making my own function so I could understand it. However the test() function does not work but does compile.

I testing, useing String.println I have found that ret contains some random charater and is not "adding" the str[]. I have tried variaous forms of this using *ret, ret[] in various combinations. At one point I had ret equal to str[] but only one character long and the next character simply replaced the first instead of adding to it.

So I tried using a String r; and setting r += str[] and the result was "473.55" in the serial window SUCCESS! Or so I thought. LOL After changing return atof(ret); to return atof(r); I get a compile error of "Cannot convert 'String' to 'const char*' for argument '1' to 'double atof(const char*)' saidly - It's back to the drawing board.

So i must use atof() with a char array and not a string, but I can't seem to turn a string into a char* anyways, to make a long story short, these are the parts I'm struggling with in understanding.

  1. In the function InStr() it is called with float f_Y = atof(str+InStr('Y', str)+2); the part in bold I am not sure what it is doing and why it is necessary for it to compile. Without it I get a invalid conversion from 'int' to 'const char*' error. The +2 part I assume is telling the function to start 2 positions from where it finds 'Y'.

  2. What do I need to change to get the test() function to work?

  3. The description for atof() states "Convert string to double" should it not be Convert Char* to double? It produces a compile error when using a string.

I'm not understanding the key differences between a Char, a Char Array, and a String or String Array. (Isn't a string just a char array?)

Thanks for any help!

Question, what are you trying to do?

From what I can tell by looking at InStr(), its looking for a particular char within the string, and how many times it occurs. Yours, I dont know.

HazardsMind: Question, what are you trying to do?

From what I can tell by looking at InStr(), its looking for a particular char within the string, and how many times it occurs. Yours, I dont know.

It is taking the incoming string, (which will eventually come from the serial port) "X:473.55Y:362.45Z:450.20E:1.11" and looking for what value X, Y, Z and E are.

In other words when I ask my 3D printer the location of the motors I send a M114 code and the printer replies with a string formatted like that above. The numbers will be whatever value the motors are in at the time. I want to be able to break it up so I can pick out the value of X, Y, Z, and E.

So; X = 473.55 Y = 362.45 Z = 450.20 E = 1.11

I'm not totally sure how InStr() is working, but in mine I am trying to look for a start char, read the value until the end char and put that value into *ret.

You examine the characters in the string one at a time looking for your unique start of number like X, Y and so on. You then skip one place for the :, then you gather the following characters into a number until you hit a non numeric characters. If it is a decimal point carry on inputting the fractional part. If it is a line terminator then it is the end of your Gcode message, if not start the procedure again from your current point looking for your unique start of number like X, Y.

Grumpy_Mike: You examine the characters in the string one at a time looking for your unique start of number like X, Y and so on. You then skip one place for the :, then you gather the following characters into a number until you hit a non numeric characters. If it is a decimal point carry on inputting the fractional part. If it is a line terminator then it is the end of your Gcode message, if not start the procedure again from your current point looking for your unique start of number like X, Y.

Correct, that's what I am doing. What I don't understand is why InStr() works and test() does not and what am I doing wrong that makes test('X','Y',str) return 0.00; instead of 473.55?

So why not just use strtok()? It will do everything practically for you. The only thing that it won't do, is look at individual incoming chars, it needs to be a complete string. So what you can do is, store everything first, then split the data.

HazardsMind: So why not just use strtok()? It will do everything practically for you. The only thing that it won't do, is look at individual incoming chars, it needs to be a complete string. So what you can do is, store everything first, then split the data.

Good God man - Your a genius!

This works,

float cX = atof(strtok(str,":EZXY"));
  float cY = atof(strtok(NULL,":EZXY"));
  float cZ = atof(strtok(NULL,":EZXY"));
  float cE = atof(strtok(NULL,":EZXY"));
  Serial.println(cX);
  Serial.println(cY);
  Serial.println(cZ);
  Serial.println(cE);

Simple. :)

I will need to put this into a function then so I can specify different delimiters but should work. Awesome - thanks!

I'd still like to know what I am doing wrong on the other test() function however, if for nothing else but to educate myself.

I believe it is the while condition. You are mixing pointer indexing and array indexing. str should always point to the first character of the array. I think you want str.*

KeithRB: I believe it is the while condition. You are mixing pointer indexing and array indexing. str should always point to the first character of the array. I think you want str.* [/quote] Well, I tried that too. Same results. Notice in the function that does work, InStr() is is also while(*cur). Thanks though.

Here is another question.

strtok() doesn't work with strings, so How do I get the number out of a string like this;

String = "M114";

int Code = [I want this to extract the 114 from the String "M114]

Well, I tried that too. Same results. Notice in the function that does work, InStr() is is also while(*cur).

InStr is incrementing cur, you are not incrementing str, you are incrementing i.

strtok() works like this.

your string "123,456:789." strtok will look in the string and actually look for ", : ." If strtok sees any of those characters, it will return to the first address it started from, and out put the data up to that split char. Now yours, being that it starts with a char and then the number follows, it will not work correctly. This is unless you maybe have a dummy variable, and then on the next variable you store the correct data. But it would just be easier to just send the numbers first and then the splitting char.

So from M117 to 117M

HazardsMind:
strtok() works like this.

your string “123,456:789.”
strtok will look in the string and actually look for “, : .” If strtok sees any of those characters, it will return to the first address it started from, and out put the data up to that split char. Now yours, being that it starts with a char and then the number follows, it will not work correctly. This is unless you maybe have a dummy variable, and then on the next variable you store the correct data. But it would just be easier to just send the numbers first and then the splitting char.

So from M117 to 117M

No I mean strtok() won’t work with a String type, only a char type. i.e.
this compiles and works,

char *str = "X:473.55Y:362.45Z:450.20E:1.11";
float cX = atof(strtok(str,":EZXY"));
Serial.println(cX);

This does not,

String str = "X:473.55Y:362.45Z:450.20E:1.11";
float cX = atof(strtok(str,":EZXY"));
Serial.println(cX);

it produces a cannot convert ‘String’ to ‘char*’ for argument ‘1’ to ‘char* strtok(char*, const char*)’ [/i]compile error.
And I can’t change how the M code is sent, it must be “M117”, so How do I get the Number from the String?
This is what I came up with but is it the best way?
**_ <em>**String sCommand = "M117"; // this is for testing sCommand = sCommand.substring(1); int LastCommand = sCommand.toInt(); Serial.print(LastCommand);**</em> _**
Result is 117

Your incoming string is going to be individual chars, not a full string. So like I said before, save the incoming chars into a char array and then split it up. As for the M117, you can use substrings, but will it work when you enter the string via serial monitor?

Note: it may need a terminating character.

HazardsMind: As for the M117, you can use substrings, but will it work when you enter the string via serial monitor?

Note: it may need a terminating character.

I am sending it to the printer over the Serial, basically

#define CODE_HOME   "G28"
// Followed by a bunch more
int LastCommand = 0;

// later
SendGcode(CODE_HOME);

// FUnction SendGCode()
int SendGcode(String sCom)
{
  Serial.println(sCom);
  // I want to store the last code sent to be used in a switch/case so I want to get the number of the code
  sCom = sCom.substring(1);
  LastCommand = sCom.toInt();
}

It all seems to work good in testing but I don't have all the codes into the system yet, just some basic ones. I want it to be efficient so I am not slowing down the printer or missing incoming info.

I want it to be efficient

But, you don't want to bother learning about C strings, and would rather rely on the String class as a crutch. Well, good luck with writing efficient code that way.

PaulS:

I want it to be efficient

But, you don't want to bother learning about C strings, and would rather rely on the String class as a crutch. Well, good luck with writing efficient code that way.

Wow, that's blunt and very far from the truth. I am trying to learn - that's why I am here. I've learned a lot already (I know have a better understanding of what * is). Even in my first post on this thread I stated:

I'm not understanding the key differences between a Char, a Char Array, and a String or String Array. (Isn't a string just a char array?)

I was even asking why one works and one doesn't. When did I ever say I didn't want to learn? It's not like I posted my entire script and asked you all to rewrite it. I've searched looking for tutorials on the differences between the char and string but many are very vague and don't give examples, I learn best by example. I own a few C/C++ books and I get a lot of info from here http://www.cplusplus.com/info/

My apologies if I made anyone feel like I was trying to get you to do the work so I didn't have to learn - not my intention. So please if you have a link to a good tutorial on the differences of char and String with examples, please post them and I won't bother you with this anymore.

My apologies for coming down so harshly. I did not read the entire thread.

A String is a class. That class performs dynamic memory allocation to create space to hold characters. It implements some functions that replicate functionality provided by the string functions that are available on the Arduino.

A string is a NULL terminated array of chars. Any C book will have a whole chapter on strings, or I'd leave it on the shelf.

While trying to learn C programming (or C++) from the internet is an admirable goal, nothing beats a book, with examples and explanations. Nothing beats typing in those examples and executing them, changing the input to see what results that has.

I have half a dozen books on each language I can program in. None of them are cheap, but each of them was worth the purchase price.

Just recently, I got a Mac, with the goal of learning to write iPhone apps. I have already bought 5 books on iOS programming, because no one book is perfect. Each covers the topic from slightly different perspectives. The most important difference between a book and tutorial on the internet, though, is then length. The books I have range from 500 to 1000 pages. An internet tutorial of that length would bore be to tears before I was 10 % of the way through it.

Books are portable. The internet is not. Books don't need batteries, either.

My point? Get a book. Then, get another one. Best investment you'll ever make.

Any C book will have a whole chapter on strings, or I'd leave it on the shelf.

Natures warning sign that strings may not be so simple to implement for new arduino users.

zoomkat:

Any C book will have a whole chapter on strings, or I'd leave it on the shelf.

Natures warning sign that strings may not be so simple to implement for new arduino users.

That chapter will explain everything to do with C string arrays.

The usual use of C++ String objects is based on not knowing what's going on 'inside the box' and has led to more "Help! I have a Problem" threads than any other programming issue here. The reason is simple: the average Arduino does not have enough RAM to do much when C++ Strings are used.

BTW, haven't you learned the simplicity of C strings yet? You've been here for years....