Go Down

Topic: How do I replace 1 or more characters ina char * array? (Read 4053 times) previous topic - next topic

Sand_HK

Before I start, I am really sorry for such a novice question. I am new to C and I am having difficulty getting my head around strings and pointers.
I have spent the last few days researching this, including a trip to the library, and I still cannot get it.

How do I replace 1 or more characters in a char * array?

here is a simple test sketch
Code: [Select]



void setup()
{
  Serial.begin(9600);
  while (!Serial) {
  ; // wait for serial port to connect. Needed for Leonardo only
  }
 
 
Serial.print("Start\n");

char* menuList[10][21]   =  { '\0' };

*menuList[1] = "test";
*menuList[2] = "2222222222";
Serial.print("menuList[1] = ");  Serial.println( *menuList[1] );
Serial.print("menuList[2] = ");  Serial.println( *menuList[2] );
// the above works fine.

//does not work - menuList[1] is unchanged
*menuList[1][1] = '9';
Serial.print("menuList[1] = ");  Serial.println( *menuList[1] );

// does not compile.  error says: lvalue required as left operand of assignment
&menuList[1][1] = '9';

// does not compile.  error says: invalid conversion from 'char' to 'char*'
*&menuList[1][1] = '9';


}


void loop() {;}




I understand the errors I am getting I just don't know what the correct format is.


My actual application is to replace the HH, MM and SS in a menu option:

Code: [Select]

menuList[1] = "Start time:00:00:00";
menuList[2] = "Stop time: 00:00:00";



I can convert INTs to strings (hours, mins, secs) and now I want to insert them into the menuList strings


Any help would be really appreciated.


PS Some kind of string replace function would be marvellous...


Sand_HK


strcpy()


I thought strcpy only copied complete strings. Can it also be used to copy sections of a string as well?





Arrch



strcpy()


I thought strcpy only copied complete strings. Can it also be used to copy sections of a string as well?


Ends of them. You could also use strncpy() to replace parts of strings, but I suspect what you're looking for is sprintf().

PaulS

Quote
char* menuList[10][21]   =  { '\0' };

This is a two dimensional array of pointers. Is that what you REALLY want?

This, by itself, allocates NO memory for the pointers to point to.
The art of getting good answers lies in asking good questions.

Sand_HK

Arrch, thanks for the replies and I think you have put me on the right track; need to build up the string rather than copy individual bits to an existing string.


Here are working samples I have:

Code: [Select]



void setup()
{
 Serial.begin(9600);
 while (!Serial) {
 ; // wait for serial port to connect. Needed for Leonardo only
 }

 
Serial.print("Start\n");
 
createTimeByStrcat(5000);

createTimeBySprintf(5000);

Serial.print("\n\nEND\n");
}


void loop()
{
while(true) {}
}




void createTimeByStrcat( long tSecs )
{

 char tempString1[3] = "\0";
 char tempString2[20] = "\0";
 
 int hours =int(  tSecs / 3600);
 int mins = int( (tSecs - (hours*3600) ) / 60   );
 int secs = int( tSecs - (hours*3600) - (mins *60) );
 
 Serial.print("hour = "); Serial.println(hours);
 Serial.print("mins = "); Serial.println(mins);
 Serial.print("secs = "); Serial.println(secs);
 
 itoa (hours,tempString1,10);
 if (hours<10) { strcat(tempString2, "0");        }
 Serial.print("tempString1 = "); Serial.println(tempString1);
 strcat(tempString2, tempString1);
 strcat(tempString2, ":");

 itoa (mins,tempString1,10);
 Serial.print("tempString1 = "); Serial.println(tempString1);
 strcat(tempString2, tempString1);
 strcat(tempString2, ":");
 
 itoa (secs,tempString1,10);
 Serial.print("tempString1 = "); Serial.println(tempString1);
 strcat(tempString2, tempString1);
 Serial.print("tempString2 = "); Serial.println(tempString2);

}


void createTimeBySprintf(long tSecs)
{
 Serial.print("\nBy sprintf\n");
 char tempString1[20] = "\0";
 int n = 0;
 int hours =int(  tSecs / 3600);
 int mins = int( (tSecs - (hours*3600) ) / 60   );
 int secs = int( tSecs - (hours*3600) - (mins *60) );
 
 Serial.print("hour = "); Serial.println(hours);
 Serial.print("mins = "); Serial.println(mins);
 Serial.print("secs = "); Serial.println(secs);

 if (hours<10)
 {n=sprintf (tempString1, "0%d:%d:%d", hours, mins, secs);}
 else
 { n=sprintf (tempString1, "%d:%d:%d", hours, mins, secs); }
 Serial.print("n = "); Serial.println(n);
 Serial.print("tempString = "); Serial.println(tempString1);
}



Both methods work but I noticed that using sprintf uses a lot more memory.
strcat -  3,248 bytes (of a 30,720 byte maximum)
sprintf - 5,248 bytes (of a 30,720 byte maximum)



here is the output:

Code: [Select]

Start
hour = 1
mins = 23
secs = 20
tempString1 = 1
tempString1 = 23
tempString1 = 20
tempString2 = 01:23:20

By sprintf
hour = 1
mins = 23
secs = 20
n = 8
tempString = 01:23:20


END




Sand_HK


Quote
char* menuList[10][21]   =  { '\0' };

This is a two dimensional array of pointers. Is that what you REALLY want?

This, by itself, allocates NO memory for the pointers to point to.


Hi Paul,

I understand this and I thought the following lines allocate the values. Or are they doing something else and because it seems to work it is fooling me?

char* menuList[10][21]   =  { '\0' };

*menuList[1] = "test";
*menuList[2] = "2222222222";

The full sketch (not posted) has 10 menu options of 20 characters each, hence the array. All are set by using menuList[1] = "Menu option"; menuList[2] = "next menu option"; etc etc. This works as long as I do not try to change them.


I have a solution for my immediate problem but would really like to get to grips with arrays and pointers. This afternoon while reading (away from the computer) I thought I had it (*s and &s) but tonight (at the computer) I cannot get anything to work.







UKHeliBob

Why the 2 dimensional array ?

If you really want to you can change the contents of a string directly
Code: [Select]

char testArray[] = {"An array to test"};
 
void setup()
{
  Serial.begin(115200);
  Serial.println(testArray);
  testArray[3] = 'A';
 
  char aChar = 'R';
  testArray[4] = aChar;

  substituteChars("RAY", testArray ,5, 3);

  char string[] = {"TEST"};
  substituteChars(string, testArray ,12, 4);
  Serial.println(testArray);
}

void loop()
{
}

void substituteChars(char source[], char destination[], byte startAt, byte length)
{
  for (int pos = 0; pos < length; pos++)
  {
    destination[startAt + pos] = source[pos];
  }
}
Whether it is a good idea is another matter.

PaulS/Arrch - is there anything inherently wrong with doing it this way, apart from the chance of getting the array index wrong at some point, of course.
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Sand_HK

The 2 dimensional array makes the menu system easier for me to handle.
I am using a 4x20 LCD and the menus are up to 10 lines long. The arrays make it easy for me to scroll the menu options up and down screen:

Code: [Select]

for (int i = 1; i <= 3; i++)
  {  lcd.setCursor(0, i);   lcd.print("                    "); // 20 spaces to clear the line
     offset = i+(topPos-1); // topPos is what ever menu option is in screen row 1
     lcd.setCursor(1, i);   lcd.print( menuList[offset] );
     if (haveFlags == 1) {     lcd.setCursor(16, i);  lcd.print(  flagVal[ tempFlag[offset]  ] );  } // displays on, off or "   ";
  }


The top line of the screen is static (menu title) the other 3 lines scroll up and down.

I do not know if this is the best way but this method is easy for me to understand and implement.


I can replace characters in single dimension arrays (as per your example) but cannot get it to work in 2 dimension arrays. I don't fully understand pointers yet.


I have decided to use the strcat method. This works and uses less memory than sprintf. Memory issues are why I got in to this mess in the first place. I have a working sketch that uses Strings but I kept running out of memory so I started changing all the Strings to strings. Didn't realize it wasn't that straight forward.


Anyway, thanks for the replies and especially to Arrch who pointed me the right direction.



JarkkoL

#9
Jun 27, 2013, 05:37 am Last Edit: Jun 27, 2013, 06:22 am by JarkkoL Reason: 1
You shouldn't use 2d array of char*'s based on what you are saying you are trying to do since it defines a 2d array of strings but your menu is just 1d array. Below is the code what I believe you should be doing instead.
Code: [Select]

enum
{
  menuitem_play, // use what ever names you want for these to make the code more readable
  menuitem_stop,
  menuitem_time,
  menuitem_4,
  menuitem_5,
  menuitem_6,
  menuitem_7,
  menuitem_8,
  menuitem_9,
  menuitem_10,
  //----
  menuitem_size
};

static const char *s_menu_items[menuitem_size]={0};
static char s_play_time[20];
static uint8_t s_menu_pos=0;

void setup()
{
  // setup menu
  s_menu_items[menuitem_play]="play";
  s_menu_items[menuitem_stop]="stop";
  s_menu_items[menuitem_time]=s_play_time;
  //...
}

void loop()
{
  // update s_play_time string here
  //...

  // write menu at current position to LCD
  for(uint8_t i=0; i<4; ++i)
  {
    lcd.setCursor(0, i);
    lcd.print("                    ");
    uint8_t menu_idx=s_menu_pos+i;
    if(menu_idx<menuitem_size && s_menu_items[menu_idx])
    {
      lcd.setCursor(0, i);
      lcd.print(s_menu_items[menu_idx]);
    }
  }
}


Go Up