Link to home
Start Free TrialLog in
Avatar of tomega3
tomega3

asked on

How to add N number of trailing characters to a C char array or C++ String object

I have a char array (C) or a String object (C++)

char myStr1[33] = ""   // buffer holds 32 chars plus null terminator
or
String myStr2 = ""; myStr2.reserve(33); // buffer holds 32 chars plus null terminator

assign some text to the string:
myStr1 = "ABCD";
myStr2 = "ABCD";

now I want to add trailing spaces (or the character Q) to both strings to fill up to to the max chars supported by the arrays (32)
32-4 = 28

want myStr1 = "ABCDQQQQQQQQQQQQQQQQQQQQQQQQQQQQ"
this should fill myStr1 with 32 chars and leave room for the null terminator

In Oracle plsql I do this by
rpad(myStr1,'Q',28);

How to do this in C or C++ ?  Must I use a for loop?

Thanks
ASKER CERTIFIED SOLUTION
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I don't have a C/C++ compiler so I can't test for you but:

from here:

http://www.unix.com/programming/144593-how-right-pad-zeros-using-sprintf.html
   printf("%s%019s\n", buffer, "0");

becomes
printf("%s%033s\n", myStr1 , "Q");


reference:
http://www.cplusplus.com/reference/clibrary/cstdio/printf/
Hi tomega,

You can do this a bit easier if you want to take advantage of some of the underlying (C) math.


  char MyString[33];

  strcpy (MyString, "ABCD");

  strcat (MyString, "QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ" + strlen (MyString));

In the snippet above, the first two lines are obvious.  The 3rd line takes the 32-character string of a 'Q' characters, adds the length of the existing string in MyString to the starting address of the 'Q string', and copies the remainder of the characters, filling the array.

If the last line is replaced with:

  strcat (MyString, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"+ strlen (MyString));

The resulting string would be:

  ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef

If the original string was "1234" the resulting string would be:

  1234EFGHIJKLMNOPQRSTUVWXYZabcdef

If the original string was "123456789" the resulting string would be:

  123456789JKLMNOPQRSTUVWXYZabcdef


Using all "Q" in the string just results in the buffer being filled with 'Q' characters.  No need to loop or write a separate function.  


Good Luck,
Kent
@ged325 that will produce this

abc00000000000000000000000000000000Q

@Kdo Except you have to create the padding string first and if that is going to be variable you are back to a loop.
Hi Julian,

As long as the padding string is in volatile memory (not generated as a compilation constant in read-only memory), one can change the string any time that it's necessary.

#define PADLENGTH 32

  char PadString[PADLENGTH+1];

//  Initial setup of PadString with "Q" as the padding character.

  memset (PadString, 'Q', PADLENGTH);
  PadString[PADLENGTH] = 0;  // End of string terminator.


//  Change the pad character to 'R'

  memset (PadString, 'R', PADLENGTH);


As long as the length doesn't change, all that's valid. If the length does/can change, a minor enhancement is required.


Kent

  PadString
memset being the key missing component....

In which case you could also do this
#define PADLENGTH 32
...
char myStr1[32] = "abcd";
int len = strlen(myStr1);
memset(&myStr1[len], 'Q', PADLENGTH - len - 1);

Open in new window

Yep.  There are a lot of valid solutions.

Note that the requirement is for 32 characters + string terminator, so your code should probably be:

#define PADLENGTH 32
...
char myStr1[PADLENGTH+1] = "abcd";
int len = strlen(myStr1);
memset(&myStr1[len], 'Q', PADLENGTH - len);
                                           

Kent
Avatar of tomega3
tomega3

ASKER

Hi all, thanks for all the examples.
The loop example from JulianH has a lot of flexiblity if the number of characters to pad changes and or the actual character to pad can change.

I looked deeper into the example proposed by ged325 and found the following works to pad with the space char in a sprintf format string:

char      myRpadFormatStr[]      = "%-20s";
sprintf(myoutStr1, myRpadFormatStr, outStr1);    // will right pad with up to 20 spaces

it does not work for any other pad character only the space char.

I also like the solution by kdo, it looks to be more memory efficient than a loop.
kdo can you provide an example of the enhancement you mentioned when the length does/can change?

I have tried right padding with space chars using a C++ string object using a loop and the String.concat method. It seems to work but including it eats up a lot of program memory.
I am also not sure if I need to null terminate the padded string after the loop finishes
Example:
String mytestStr = "";
mytestStr.reserve(33); // 32 chars plus null terminator
mytestStr = "ABCD";

void myRpad()
{
    int lendif = (32) - (mytestString.length());
    int ii;
    for(ii = 0; ii < lendif; ii++)
    {
      mytestString += ' ';    // right pad with spaces from end of mytestStringto last char
    }
   // do I need to null terminate the padded string?
}

I look forward to your comments,  and any suggestions on a routine to left pad (pad with leading spaces or characters.

Thanks
I also like the solution by kdo, it looks to be more memory efficient than a loop.
You cant get away from a loop every character in the array has to be visited. While memset may be marginally more efficient in how it achieves this - it is negligible.

do I need to null terminate the padded string?
No but if efficiency is a criteria then using String is not the way to go - iterating through a char is the simplest approach.
Avatar of tomega3

ASKER

Works very well, it is efficient, and flexible in length of padding and what character to pad with. I am looking forward to see if it can be modified to add leading spaces or other character, thus we will have 2 routines, one for RPAD a string and one for LPAD a string.
Thank you very much to everybody for your help, especially jullianH

Tom
Hi Tom,

Had to set away from my desk for a while.  Apologies.

The only requirement to managing strings/buffers of differing lengths is that the memory allocated for the padding array must be large enough to handle the longest padding field.  (If you're going to pad fields of 30, 50, and 90 characters, a buffer of 91 characters will handle them all.)  If the padding (for any one string) is always the same character it's not necessary to fill an array -- you can simply use memset () to put the desired character into all of the character positions, and then set the string terminator in the last position.

Actually, a loop is somewhat more memory efficient that a padding buffer, and if the padding is anything but a block of the same character, the padding buffer beats the loop.  For such a small object, both are negligible on a machine with 4GB of memory and a 3GHz process.  :)

Any solution will require a loop.  You can write one yourself, you can rely on the loop inherent to a supporting function (memset, strcpy, etc.), or you can rely on a loop built into the hardware (i.e. fill memory with character).  Writing your own loop is almost always wrong for several reasons.  1)  It's been done.  There's no need to reinvent the wheel;  2)  The loop in the system function is probably better optimized than you will write;  and 3) You're adding complexity.  You're accomplishing in 4 lines of code what can be done in one.

As to whether the loop is controlled by code in the function or in the hardware, that's out of your control.  The compiler will make the best use of the instructions available to it.



Good Luck,
Kent
Avatar of tomega3

ASKER

Thanks Kent, I will give the memset solution a try later today. I have jullianH's solution working well.

I am running this C code on an Arduino UNO running at 16Mhz with1K of Ram, 32K of flash or pgm memory. The String object solution I tried just ate up to much ram and flash. Thats why I am back to char arrays that are null terminated. I use sprintf to format strings for display,

The reason I need to pad right with spaces is that the string of chars will be written to a LCD display. The display does not have a clearLine routine just a clear the entire screen. So I figured I would right pad with spaces to clear out any remaining characters left on a line when I write a smaller string of chars to the same line.

Can you suggest a way to add leading spaces to a string? I can't wrap my head around rewritting memset or the loop jullian suggested to go backwards.

Tom
Hi Tom,

16MHz?  1K RAM?  What are you using?  This sounds like a controller or specialty chip.

If those numbers are real, you probably want to avoid the function and use memcpy.  It'll take less memory and with only 1K, you don't have much to spare.  (I suspect that you meant 1M if it's a controller chip, and 1G if it's a desktop though.)

Applying leading spaces is an interesting exercise.  To pad the correct number of spaces you must know the number of characters to pad, and that usually requires that you know that length of the buffer (in this case the display width) and the number of characters in the string to be written.  If you want to center a 10-character string in an 80-column display, you'd need 35 leading blanks, followed by the string, and (optionally) another 35 blank characters (to erase the line or preserve cursor position/formatting).

Assuming that the display width is constant (the size of the screen or window doesn't change) that item's trivial.  The next thing to do is to format the text that you're going to display in a string (character) buffer.  snprintf works well for this and it has a remarkably friendly side effect -- it returns the number of characters written to the string.  i.e., the string length.

Perhaps the easiest and most portable solution is to write a small function that does it for you.

  void PrintFixedWidth (int ScreenWidth, char *String, int Length)
  {
    int  Spaces = ScreenWidth - Length;  // total number of leading and trailing spaces needed
    int  LeadingSpaces = Spaces / 2;
    int  TrailingSpaces = Spaces - LeadingSpaces;

    print ("%*.*s%s%*.*s", 
      LeadingSpaces, LeadingSpaces, " ", 
      String, 
      TrailingSpaces, TrailingSpaces, " ");
  }

Open in new window


This takes advantage of C's variable length format field (*.*) to write the correct number of spaces before the string.  The value for TrailingSpaces is computed separately because the total number of spaces may be an odd number, in which case the leading and trailing counts will differ by 1.


There are lots of workable solutions.  Something like this would be where I'd start.


Kent
Avatar of tomega3

ASKER

Hi Kent,
Yes I am working on a microcontroller called Arduino Uno,
see http://arduino.cc/en/Main/ArduinoBoardUno

Its about $30.00 at radioshack and comes with a free open source IDE and a bunch of open source libraries. Its IDE compiles most C and C++ code. I was wrong, the UNO has 2K of ram, 1K of eeprom, and 32k of flash aka pgm memory and it has a 16Mhz clock.
Its a lot of fun. I have not done any C code since the early 1990's, I do mostly Oracle PLSQL.
Relearning C code and using C++ is refreshing but also challenging.

I have a routine in my code that reports the amount of available ram at run time.
I tried 3 different methods of right padding see below

I am padding at most 19 spaces to a 1 char string, less spaces if string is longer

1. // this works to rpad with up to 20 spaces using sprintf format string "%-20s"
   // outstr1 is a passed in ptr to a string, myoutStr1 is declared as char myoutStr1[32];
sprintf(myoutStr1, myRpadFormatStr, outStr1);    // right pad with upto 20 spaces    
lcd.print(myoutStr1);

2. // outstr1 is a passed in ptr to a string, myoutStr1 is declared as char myoutStr1[32];
sprintf(localtempoutString3,"%s",outStr1);   // need to do this so memset works properly
memset(&localtempoutString3[len1],' ',mycols - len1);
sprintf(myoutStr1,"%s",localtempoutString3); // null terminate the string
lcd.print(myoutStr1);      
     
3. // outstr1 is a passed in ptr to a string, myoutStr1 is declared as char myoutStr1[32];
// this works to rpad up to 20 spaces when outStr1 is a char array of        //MAXARRAYSIZE which is larger than mycols
sprintf(localtempoutString3,"%s",outStr1);  // need to do this so padstring works    
padstring(localtempoutString3, 20, ' ');
lcd.print(localtempoutString3);

No difference in RAM usage,

I will stay with jullianH's loop for now, I can read it and know what its doing.

These little microcontrollers are a lot of fun. I started off my career in embedded system and assembly code.  This arduino even has a way to store fixed char strings in the PGM memory so to free up more ram. I guess we all got spoiled when Intel gave us more than 640K or ram and a 10MB harddisk.

Thanks again for your help. I will try to find a loop like jullians to do the pad with leading spaces, I know my string length,  my display width is fixed, either 16 chars or 20 chars, so I guess an LPAD loop function is possible.

Tom