firstworks Programming with Rudiments using the charstring class

Using the charstring class

- Introduction
- Manipulating Character Strings
- Comparing Character Strings
- Finding Data in Character Strings
- Transforming Character Strings
- Parsing Character Strings
- Converting Numbers and Amounts
- Encoding Character Strings

### Introduction

The charstring class provides static methods for manipulating C-style character strings.

In addition to some unique methods, analogs for the standard C string functions are provided. However, unlike the standard C string functions, the charstring methods are NULL safe. Your application will not crash if a NULL is passed in, and instead, will give intuitive results.

### Manipulating Character Strings

The charstring class provides methods for zeroing, duplicating, appending, copying data to, printing formatted data to, and determining the lengths of character strings.

> #include <rudiments/charstring.h>
> #include <rudiments/stdio.h>
> int main(int argc, const char **argv) {
> char buffer[32];
> // zero the buffer
> charstring::zero(buffer,sizeof(buffer));
> // append strings, integers and floats
> charstring::append(buffer,"Hello ");
> charstring::append(buffer,"there!",6);
> charstring::append(buffer," ");
> charstring::append(buffer,(uint64_t)1);
> charstring::append(buffer,(uint64_t)2);
> charstring::append(buffer,(uint64_t)3);
> charstring::append(buffer," ");
> charstring::append(buffer,1.234,4,3);
> stdoutput.printf("buffer: %s\n",buffer);
> stdoutput.write('\n');
> // get length
> stdoutput.printf("length(buffer)=%d\n",charstring::length(buffer));
> stdoutput.write('\n');
> // zero the buffer again
> charstring::zero(buffer,sizeof(buffer));
> // copy to the beginning of the buffer
> charstring::copy(buffer,"Hello!");
> stdoutput.printf("buffer: %s\n",buffer);
> // copy the specified number of bytes to the beginning of the buffer
> charstring::copy(buffer,"Hello again!",6);
> stdoutput.printf("buffer: %s\n",buffer);
> // copy to the specified offset
> charstring::copy(buffer,6,"again!");
> stdoutput.printf("buffer: %s\n",buffer);
> // copy the specified number of bytes to the specified offset
> charstring::copy(buffer,12," Hi! blah blah blah",4);
> stdoutput.printf("buffer: %s\n",buffer);
> stdoutput.write('\n');
> // get length
> stdoutput.printf("length(buffer)=%d\n",charstring::length(buffer));
> stdoutput.write('\n');
> // zero the buffer again
> charstring::zero(buffer,sizeof(buffer));
> // safely copy a long string to a smaller buffer
> charstring::safeCopy(buffer,sizeof(buffer),
> "This string is longer than the buffer");
> stdoutput.printf("buffer: %.*s\n",sizeof(buffer),buffer);
> // safely copy the specified number of bytes of a
> // long string to a smaller buffer
> charstring::safeCopy(buffer,sizeof(buffer),
> "This string is longer than the buffer",36);
> stdoutput.printf("buffer: %.*s\n",sizeof(buffer),buffer);
> stdoutput.write('\n');
> // get length
> stdoutput.printf("length(buffer)=%d\n",charstring::length(buffer));
> stdoutput.write('\n');
> // zero the buffer again
> charstring::zero(buffer,sizeof(buffer));
> // print formatted data to the buffer
> charstring::printf(buffer,sizeof(buffer),"%s, %05d, %7.4f",
> "hello",100,123.4567);
> stdoutput.printf("buffer: %s\n",buffer);
> stdoutput.write('\n');
> // get length
> stdoutput.printf("length(buffer)=%d\n",charstring::length(buffer));
> stdoutput.write('\n');
> // duplicate a string
> char *completedup=charstring::duplicate("Hello there!");
> char *partialdup=charstring::duplicate("Hello there!",5);
> stdoutput.printf("complete duplicate of \"Hello there!\": \"%s\"\n",
> completedup);
> stdoutput.printf(" partial duplicate of \"Hello there!\": \"%s\"\n",
> partialdup);
> delete[] completedup;
> delete[] partialdup;
> }

### Comparing Character Strings

The charstring class also provides methods for performing various character string comparisons.

> #include <rudiments/charstring.h>
> #include <rudiments/stdio.h>
> int main(int argc, const char **argv) {
> // comparing string...
> const char * const strings[]={
> "hello","HELLO","hello there","HELLO THERE",NULL
> };
> stdoutput.write("direct comparison...\n");
> for (const char * const *s=strings; *s; s++) {
> stdoutput.printf(" does \"hello\"=\"%s\" %s?\n",*s,
> (!charstring::compare("hello",*s))?"yes":"no");
> }
> stdoutput.write('\n');
> stdoutput.write("only first 5 bytes...\n");
> for (const char * const *s=strings; *s; s++) {
> stdoutput.printf(" does \"hello\"=\"%s\"? %s\n",*s,
> (!charstring::compare("hello",*s,5))?"yes":"no");
> }
> stdoutput.write('\n');
> stdoutput.write("ignoring case...\n");
> for (const char * const *s=strings; *s; s++) {
> stdoutput.printf(" does \"hello\"=\"%s\"? %s\n",*s,
> (!charstring::compareIgnoringCase("hello",*s,5))?
> "yes":"no");
> }
> stdoutput.write('\n');
> stdoutput.write("ignoring case, only first 5 bytes...\n");
> for (const char * const *s=strings; *s; s++) {
> stdoutput.printf(" does \"hello\"=\"%s\"? %s\n",*s,
> (!charstring::compareIgnoringCase("hello",*s,5))?
> "yes":"no");
> }
> stdoutput.write('\n');
> // member of a set...
> const char * const greetings[]={
> "hello","hi","good morning",NULL
> };
> const char * const lowercaseexpressions[]={
> "hello","hi","bye","goodbye",NULL
> };
> const char * const uppercaseexpressions[]={
> "HELLO","HI","BYE","GOODBYE",NULL
> };
> stdoutput.write("considering case...\n");
> for (const char * const *le=lowercaseexpressions; *le; le++) {
> stdoutput.printf(" is \"%s\" a greeting? %s\n",*le,
> (charstring::inSet(*le,greetings))?"yes":"no");
> }
> stdoutput.write('\n');
> stdoutput.write("ignoring case...\n");
> for (const char * const *ue=uppercaseexpressions; *ue; ue++) {
> stdoutput.printf(" is \"%s\" a greeting? %s\n",*ue,
> (charstring::inSetIgnoringCase(*ue,greetings))?
> "yes":"no");
> }
> stdoutput.write('\n');
> // does one string contain another?
> const char phrase[]="the quick brown fox jumped over the lazy dog";
> const char * const lowercasewords[]={
> "quick","brown","fox","lazy","dog","hello","goodbye",NULL
> };
> const char * const uppercasewords[]={
> "QUICK","BROWN","FOX","LAZY","DOG","HELLO","GOODBYE",NULL
> };
> stdoutput.write("considering case...\n");
> for (const char * const *lw=lowercasewords; *lw; lw++) {
> stdoutput.printf(" does \"%s\" contain \"%s\"? %s\n",
> phrase,*lw,
> (charstring::contains(phrase,*lw))?"yes":"no");
> }
> stdoutput.write('\n');
> stdoutput.write("ignoring case...\n");
> for (const char * const *uw=uppercasewords; *uw; uw++) {
> stdoutput.printf(" does \"%s\" contain \"%s\"? %s\n",
> phrase,*uw,
> (charstring::contains(phrase,*uw))?"yes":"no");
> }
> stdoutput.write('\n');
> }

### Finding Data in Character Strings

The charstring class also provides methods for finding characters or other character strings within character strings.

The findFirst()/findFirstOfSet() and findLast() methods return the first/last instance of a character/string/set within a string, or NULL if no match is found.

> #include <rudiments/charstring.h>
> #include <rudiments/stdio.h>
> int main(int argc, const char **argv) {
> // first/last instances of a character or string...
> const char phrase[]="1 and 2 and 3 and 4";
> const char numbers[]="1234";
> const char *firsta=charstring::findFirst(phrase,'a');
> const char *firstand=charstring::findFirst(phrase,"and");
> const char *lasta=charstring::findLast(phrase,'a');
> const char *lastand=charstring::findLast(phrase,"and");
> const char *firstnum=charstring::findFirstOfSet(phrase,numbers);
> stdoutput.printf("in the phrase: \"%s\"...\n",phrase);
> stdoutput.printf(" the first 'a' is : \"%s\"\n",firsta);
> stdoutput.printf(" the first \"and\" is : \"%s\"\n",firstand);
> stdoutput.printf(" the last 'a' is : \"%s\"\n",lasta);
> stdoutput.printf(" the last \"and\" is : \"%s\"\n",lastand);
> stdoutput.printf(" the first number is : \"%s\"\n",firstnum);
> stdoutput.write('\n');
> }

### Transforming Character Strings

The charstring class also provides methods for transforming character strings.

> #include <rudiments/charstring.h>
> #include <rudiments/stdio.h>
> int main(int argc, const char **argv) {
> char hellothere[]=" hello there! ";
> // upper-case...
> charstring::upper(hellothere);
> stdoutput.printf("upper cased: \"%s\"\n",hellothere);
> // lower-case...
> charstring::lower(hellothere);
> stdoutput.printf("lower cased: \"%s\"\n",hellothere);
> // capitalized...
> charstring::capitalize(hellothere);
> stdoutput.printf("capitalized: \"%s\"\n",hellothere);
> // right trimmed...
> charstring::rightTrim(hellothere);
> stdoutput.printf("right trimmed: \"%s\"\n",hellothere);
> // left trimmed...
> charstring::leftTrim(hellothere);
> stdoutput.printf("left trimmed: \"%s\"\n",hellothere);
> stdoutput.write('\n');
> char paragraph[]="Hello there.\n This is a paragraph\n "
> "with random\n carriage returns\n "
> "scattered throughout.";
> // original...
> stdoutput.printf("original text:\n%s\n\n",paragraph);
> // stripped of carraige returns...
> charstring::strip(paragraph,'\n');
> stdoutput.printf("text without carriage returns:\n%s\n\n",paragraph);
> // stripped of "Hello there."...
> charstring::strip(paragraph,"Hello there. ");
> stdoutput.printf("text without \"Hello There. \":\n%s\n\n",paragraph);
> // with replacements...
> charstring::replace(paragraph,' ','_');
> stdoutput.printf("text with spaces replaced by underscores:\n%s\n\n",
> paragraph);
> char paddedtext[]=" hello ";
> // original...
> stdoutput.printf("original text: \"%s\"\n",paddedtext);
> // left-justified...
> charstring::leftJustify(paddedtext,charstring::length(paddedtext));
> stdoutput.printf("left-justified: \"%s\"\n",paddedtext);
> // right-justified...
> charstring::rightJustify(paddedtext,charstring::length(paddedtext));
> stdoutput.printf("right-justified: \"%s\"\n",paddedtext);
> // centered...
> charstring::center(paddedtext,charstring::length(paddedtext));
> stdoutput.printf("centered: \"%s\"\n",paddedtext);
> stdoutput.write('\n');
> const char unpaddedtext[]="hellothere";
> // original...
> stdoutput.printf("original text: \"%s\"\n",unpaddedtext);
> // left-padded
> char *leftpadded=charstring::pad(unpaddedtext,' ',-1,15);
> stdoutput.printf("left padded text: \"%s\"\n",leftpadded);
> delete[] leftpadded;
> // right-padded
> char *rightpadded=charstring::pad(unpaddedtext,' ',1,15);
> stdoutput.printf("right padded text: \"%s\"\n",rightpadded);
> delete[] rightpadded;
> // center-padded
> char *centerpadded=charstring::pad(unpaddedtext,' ',0,15);
> stdoutput.printf("center padded text: \"%s\"\n",centerpadded);
> delete[] centerpadded;
> }

### Parsing Character Strings

The charstring class also provides methods for splitting character strings, finding substrings and inserting text into a character string.

> #include <rudiments/charstring.h>
> #include <rudiments/stdio.h>
> int main(int argc, const char **argv) {
> const char str[]="All along the untrodden paths of the future...";
> // split...
> char **parts;
> uint64_t partcount;
> charstring::split(str," ",true,&parts,&partcount);
> stdoutput.printf("original string:\n %s\n",str);
> stdoutput.printf("split on space:\n");
> for (uint64_t i=0; i<partcount; i++) {
> stdoutput.printf(" %s\n",parts[i]);
> }
> stdoutput.write('\n');
> for (uint64_t i=0; i<partcount; i++) {
> delete[] parts[i];
> }
> delete[] parts;
> // substring...
> char *substring1=charstring::subString(str,14);
> char *substring2=charstring::subString(str,14,28);
> stdoutput.printf("string starting at index 14: %s\n",substring1);
> stdoutput.printf("string from index 14 to 21 : %s\n",substring2);
> stdoutput.write('\n');
> delete[] substring1;
> delete[] substring2;
> // insert string...
> char *newstr=charstring::insertString(str,
> ", I can see the footprints of an unseen hand",43);
> stdoutput.printf("string after insert:\n %s\n",newstr);
> stdoutput.write('\n');
> delete[] newstr;
> }

### Converting Numbers and Amounts

The charstring class also provides methods for converting numbers and dollar amounts to and from character strings.

> #include <rudiments/charstring.h>
> #include <rudiments/stdio.h>
> int main(int argc, const char **argv) {
> // conversion of numbers to strings...
> char *intstr=charstring::parseNumber((uint64_t)12345);
> char *floatstr=charstring::parseNumber((float)12.345,5,3);
> stdoutput.printf("numbers as strings: %s, %s\n",intstr,floatstr);
> stdoutput.write('\n');
> delete[] intstr;
> delete[] floatstr;
> // conversion of strings to numbers...
> int64_t intnum=charstring::toInteger("12345");
> uint64_t uintnum=charstring::toUnsignedInteger("12345");
> long double floatnum=charstring::toFloat("12.345");
> stdoutput.printf("strings as numbers: %lld, %lld, %5.3Lf\n",
> intnum,uintnum,floatnum);
> stdoutput.write('\n');
> // identification of numeric strings...
> const char * const numbers[]={
> "1","-1","1.1","-1.1","one","hello",NULL
> };
> for (const char * const *n=numbers; *n; n++) {
> stdoutput.printf("%s %s a number\n",*n,
> (charstring::isNumber(*n))?"is":"is not");
> stdoutput.printf("%s %s an integer\n",*n,
> (charstring::isInteger(*n))?"is":"is not");
> }
> stdoutput.write('\n');
> // integer lengths
> uint64_t integers[]={
> 1,23,456,7890,12345,678901,0
> };
> for (uint64_t *i=integers; *i; i++) {
> stdoutput.printf("it would take %d bytes to store "
> "%lld as a string\n",
> charstring::integerLength(*i),*i);
> }
> stdoutput.write('\n');
> // dollar amounts
> const char dollarstr[]="$123.45";
> int64_t pennies=charstring::convertAmount(dollarstr);
> char *dollars=charstring::convertAmount(pennies);
> stdoutput.printf("%s as pennies: %lld\n",dollarstr,pennies);
> stdoutput.printf("%lld pennies as dollars: %s\n",pennies,dollars);
> }

### Encoding Character Strings

The charstring class also provides methods for escaping, encoding and obfuscating character strings.

> #include <rudiments/charstring.h>
> #include <rudiments/stdio.h>
> int main(int argc, const char **argv) {
> // http escape...
> const char httpstr[]="string with spaces and symbols: \\{}\"\'";
> stdoutput.printf("original string:\n %s\n",httpstr);
> char *escapedstr=charstring::httpEscape(httpstr);
> stdoutput.printf("http escaped string:\n %s\n",escapedstr);
> char *unescapedstr=charstring::httpUnescape(escapedstr);
> stdoutput.printf("http unescaped string:\n %s\n",unescapedstr);
> stdoutput.write('\n');
> delete[] escapedstr;
> delete[] unescapedstr;
> // backslash-escaping of quote, backslash and space characters...
> const char path[]="\"C:\\Program Files\\Firstworks\"";
> stdoutput.printf("original path:\n %s\n",path);
> char *escapedpath=charstring::escape(path,"\"\\ ");
> stdoutput.printf("escaped path:\n %s\n",escapedpath);
> char *unescapedpath=charstring::unescape(escapedpath);
> stdoutput.printf("unescaped path:\n %s\n",unescapedpath);
> stdoutput.write('\n');
> delete[] escapedpath;
> delete[] unescapedpath;
> // base-64 encoding...
> const unsigned char text[]="All along the untrodden "
> "paths of the future...";
> stdoutput.printf("original text:\n %s\n",text);
> char *encodedtext=charstring::base64Encode(text);
> stdoutput.printf("encoded text:\n %s\n",encodedtext);
> unsigned char *decodedtext=charstring::base64Decode(encodedtext);
> stdoutput.printf("decoded text:\n %s\n",decodedtext);
> stdoutput.write('\n');
> delete[] encodedtext;
> delete[] decodedtext;
> // obfuscation...
> char data[]="sensitive data";
> stdoutput.printf("original data:\n %s\n",data);
> charstring::obfuscate(data);
> stdoutput.write("obfuscated data:\n ");
> stdoutput.safePrint(data);
> stdoutput.write("\n");
> charstring::deobfuscate(data);
> stdoutput.printf("deobfuscated data:\n %s\n",data);
> }