GLYLIB  0.3.0b
string_utilities.c
Go to the documentation of this file.
00001 /** \file string_utilities.c Functions for writing variable strings.
00002 
00003 Contains utilities that produce numbers in certain formats or that remove
00004 initial and trailing whitespace, etc.
00005 
00006 */
00007 //#include <stdio.h>
00008 //#include <stdlib.h>
00009 //#include <string.h>
00010 
00011 #include <mylib.h>
00012 
00013 /************* prune_string_whitespace() ***************/
00014 /** Function to prune leading and trailing whitespace from a string 
00015 */
00016 const char *prune_string_whitespace(const char *I){
00017 int     i=0, ///< A counter
00018         len=0, ///< Length (meaning varies)
00019         LEN=0, ///< Length of incoming string
00020         start=0, ///< Start point for pruned string
00021         stop=0; ///< End point for pruned string
00022 char    *O; ///< Outgoing string
00023 
00024 LEN=strlen(I);
00025 start=strspn(I," \t\n\r\f\v"); 
00026 //printf("LEN is %d and start is %d\n",LEN,start);
00027 if(start>=LEN){ // if the string is all whitespace
00028         O=(char*)calloc(1,sizeof(char));
00029         O[0]='\0';
00030         return O;}
00031 
00032 stop=LEN-1; ///< Begin at the last character
00033 //printf("stop is %d\n",stop);
00034 i=-1;
00035 while(i!=0){
00036         switch(I[stop]){
00037                 case ' ':
00038                 case '\t':
00039                 case '\n':
00040                 case '\r':
00041                 case '\f':
00042                 case '\v':
00043                         stop --;
00044                         break;
00045                 default:
00046                         i=0;
00047                         break;
00048                 }
00049         if(stop<0){stop=i=0;}
00050         }
00051 
00052 if(stop<start){
00053         printf("In prune_string_whitespace, stop is less than start.  This is a coding bug.\nExiting.\n");
00054         exit(1);}
00055 
00056 len=stop-start+1;
00057 O=(char*)calloc(len+1,sizeof(char));
00058 for(i=0;i<len;i++){O[i]=I[i+start];}
00059 O[len]='\0';
00060 
00061 return O;
00062 }
00063 
00064 /** Functions that produce variable-format output for numbers and strings.
00065 
00066 There must be some already-written way to do this, but I suspect I can write
00067 the functions before I can figure that out.  So, these functions will generate
00068 strings when one does not know ahead of time the output format.
00069 
00070 For example:
00071 
00072 Assume you know you need to write out a double variable in exponential 
00073 notation, but that the program will determine the proper number of 
00074 decimal places and character width.  These functions will help.
00075 
00076 So, where normally, you might write:
00077 
00078 - fprintf("%18.8e",x); 
00079         - that's a right justified exponential, 18 chars wide with 8 decimal places
00080 
00081 You can do, instead:
00082 
00083 - char j,*s;
00084 - int w,d;
00085 
00086 - w = figure_out_width();
00087 - d = figure_out_number_decimal_places();
00088 - j = figure_out_justification(); // 'L' or 'l' for left, 'R' or 'r' for right
00089 
00090 - s = strdup(get_exp_string(x,j,w,d));
00091 
00092 - fprintf("%s",s);
00093 
00094 Normally, this is a lot of bother.  But, at times, it can be useful.  
00095 
00096 I'm lazy, so the maximum number of digits that can be printed is about 145.
00097 For float values, that number is divided into pre- and post-decimal, which makes
00098 for some limits.  Hopefully, these limits are generous enough for most applications.
00099 This is hard-coded in the number->string functions below.  There are much cooler 
00100 ways to do this, and anyone who wants to code them is very welcome to do so.
00101 */
00102 
00103 /************* get_char_string() ***************/
00104 const char * get_char_string(const char *x, char j, int w){
00105 char *s=NULL,*t=NULL;
00106 int i,k;
00107 
00108 k=w-strlen(x); // difference between outgoing and incoming lengths
00109 /// If the incoming string (x) is longer than outgoing size (w),
00110 ///     write something to stderr and print what fits
00111 if(k<0){ fprintf(stderr,"\nget_char_string: incoming string is longer than requested outgoing size.\n\t  Doing the best we can.\n\n"); }
00112 s=(char*)calloc(w+1,sizeof(char)); ///< Allocate memory for new string
00113 // Since clear allocating, we don't need to worry about the final '\0', but might anyway
00114 
00115 switch (j) {
00116         case 'L':
00117         case 'l':
00118                 if(k<=0){for(i=0;i<w;i++){s[i]=x[i];}} ///< If incoming string is long
00119                 else{
00120                         t=(char*)calloc(k+1,sizeof(char));
00121                         for(i=0;i<k;i++){t[i]=' ';} // space padding
00122                         t[k]='\0'; // for safety
00123                         if((strlen(t)+strlen(x))!=w){mywhine("get_char_string: coding bug in s/t/w string handling, left.");}
00124                         strcat(s,x);
00125                         strcat(s,t);
00126                         }
00127                 break;
00128         case 'R':
00129         case 'r': 
00130                 if(k<=0){for(i=w-1;i>=0;i--){s[i]=x[i-k];}} ///< If incoming string is long
00131                 else{
00132                         t=(char*)calloc(k+1,sizeof(char));
00133                         for(i=0;i<k;i++){t[i]=' ';} // space padding
00134                         t[k]='\0'; // for safety
00135                         if((strlen(t)+strlen(x))!=w){mywhine("get_char_string: coding bug in s/t/w string handling, right.");}
00136                         strcat(s,t);
00137                         strcat(s,x);
00138                         }
00139                 break;
00140         default:
00141                 mywhine("get_char_string: unrecognized justification specification");
00142                 break;
00143         }
00144 if(t!=NULL){free(t);}
00145 s[w]='\0'; // for safety
00146 return s;
00147 }
00148 
00149 /************* get_float_string() ***************/
00150 const char * get_float_string(double x, char j, int w, int d){
00151 char *s=NULL,*t=NULL,*xs,num[151],cdig[2];
00152 int i=0,k=0,tst=0,len=0,start=0,end=0,digit=0,checkpt=0,first_non_nine=0;
00153 
00154 if(w>150){mywhine("In get_float_string: Cannot have total width greater than 150 characters.");}
00155 if(d>74){mywhine("In get_float_string: Cannot have number of decimal places greater than 74.");}
00156 cdig[0]=cdig[1]='\0';
00157 
00158 if(x>=0){
00159         tst=(int)(log10(x))+1;
00160 /*printf("int(log10(x)) is %d and tst is %d\n",(int)(log10(x)),tst);*/
00161         }
00162 if(x<0){
00163         tst=(int)(log10(-x))+1;
00164 /*printf("int(log10(-x)) is %d and tst is %d\n",(int)(log10(-x)),tst);*/
00165         }
00166 /* set for case where fabs(x)<1 */
00167 if(fabs(x)<1.0){tst=1;} 
00168 if(x==0){tst=1;}
00169 k=w-tst; // difference between outgoing length and length of integer part of float value
00170 /// If the incoming string (x) is longer than outgoing size (w),
00171 ///     write something to stderr and print what fits
00172 if(k<0){
00173         fprintf(stderr,"\nget_float_string: WARNING!!!  Integer portion of incoming value exceeds requested outgoing width.\n");
00174         fprintf(stderr,"\tReturning non-numeric string. \n\n");
00175         s=(char*)calloc((w+1),sizeof(char)); ///< Allocate memory for new string
00176         for(i=0;i<w;i++){s[i]='*';}
00177         return s;
00178         }
00179 /** Otherwise, print a really wide version of the incoming number to a really wide string
00180   Here, the string is 150 chars wide, and 74 of those chars are decimal digits.
00181   Another 74 are allocated for the integer part of the float representation.
00182   That leaves two spaces, one for a decimal point and another for '-' if needed. 
00183   At present, this function will not return a '+' sign for positive values. */
00184 sprintf(num,"%150.74f",x); ///< 150 wide, minus +/- designation and decimal point and half of that for the fraction
00185 len=tst+d+2; ///< tst=integer part ; d=fraction part ; 2 = space for . & sign (\0 comes later)
00186 if(d==0){len=tst+1;} ///< tst=integer part ; 1 = space for sign and . (\0 comes later)
00187 if(w<len){ //< print a statement if we have to give back fewer decimal places to fit the width, but do it anyway
00188         fprintf(stdout,"\nget_float_string: Float width including requested decimal places exceeds requested outgoing width.\n\tRounding.  Hope that's ok.\n\n");
00189         len=w;}
00190 
00191 start=75-tst-1; ///< the starting point in the big number 
00192 
00193 if(start<0){fprintf(stderr,"get_float_string: WARNING!! Incoming number too large to round up.\n");
00194         fprintf(stderr,"\tReturning non-numeric string.\n\n");
00195         s=(char*)calloc((w+1),sizeof(char)); ///< Allocate memory for new string
00196         for(i=0;i<w;i++){s[i]='*';}
00197         return s; // return the bad s and stop bothering
00198         }
00199 //if(start<0){mywhine("get_float_string: Problem in first assignment of start value.");}
00200 end=start+len-1; ///< the ending point in the big number
00201 if(end>=150){mywhine("get_float_string: Problem in first assignment of end value.");}
00202 
00203 checkpt=end+1; ///< check the integer where the null character will finally go
00204 //printf("checkpt is >>%d<<\n",checkpt);
00205 if((checkpt<150)&&(num[checkpt]=='.')){checkpt++;} ///< if there is a decimal point there, move over by one, 
00206                 ///< but only if that won't put us over 150 chars (shouldn't ever happen, really) 
00207 //printf("end is >>%d<< num[end] is >>%c<<\n",end,num[end]);
00208 //printf("checkpt is >>%d<< num[checkpt] is >>%c<<\n",checkpt,num[checkpt]);
00209 xs=(char*)calloc((len+1),sizeof(char)); ///< Allocate memory for new string
00210 
00211 
00212 cdig[0]=num[checkpt];
00213 digit=atoi(cdig); 
00214 /*printf("num[checkpt] (init) is %c, num[end] is %c\n",num[checkpt],num[end]);*/
00215 /// See if we have a "9X' situation where we have to round up
00216 if((num[end]=='9')&&(digit>4)){
00217                 // Check to the left until a non-nine digit is found
00218                 for(first_non_nine=checkpt-1;first_non_nine>=start;first_non_nine--){
00219                         if((num[first_non_nine]!='9')&&(num[first_non_nine]!='.')){break;}
00220                         }
00221 //printf("first_non_nine is >>%d<< num[first_non_nine] is >>%c<<\n",first_non_nine,num[first_non_nine]);
00222 //printf("first_non_nine+1 is >>%d<< num[first_non_nine+1] is >>%c<<\n",first_non_nine+1,num[first_non_nine+1]);
00223                 if((num[first_non_nine]=='.')||(num[first_non_nine]=='9')){mywhine("get_float_string: Problem with coding in first non-nine.");}
00224                 if(first_non_nine==start){// If the first space:
00225 //printf("first non-nine is at start\n");
00226                         xs[0]=num[start];///< Print the first character (space or '-') 
00227                         xs[1]='1';///< Print a 1 in the next space
00228                         /// For the rest of the string-1, for every 9 add a zero (one space over).  Copy any decimals as-is
00229                         for(i=1;i<len;i++){
00230 //printf("i is %d ; num[start+i] is %c\n",i,num[start+i]);
00231                                 switch (num[start+i]) {
00232                                         case '9': xs[i+1]='0';
00233 //printf("\t xs[1+i] is %c\n",xs[i+1]);
00234                                                 break;
00235                                         case '.': xs[i+1]='.';
00236 //printf("\t xs[1+i] is %c\n",xs[i+1]);
00237                                                 break;
00238                                         default: mywhine("get_float_string: Problem with coding in all nines switch.");
00239                                         }
00240                                 }
00241                         xs[len]='\0';///< Ensure null termination
00242 //printf("strlen(xs) is %d ; xs is >>%s<<\n",strlen(xs),xs);
00243                         }
00244                 else{
00245 //printf("first non-nine is not at start\n");
00246                         for(i=start;i<first_non_nine;i++){xs[i-start]=num[i];} ///< Copy as usual up to "first_not_nine"
00247                         /// Increment the "first_not_nine" digit 
00248                         cdig[0]=num[first_non_nine];
00249                         digit=atoi(cdig); 
00250 //printf("digit is %d ; cdig is %s\n",digit,cdig);
00251                         if((digit<0)||(digit>8)){mywhine("get_float_string: Digit outside range of 0-8 but shouldn't be.");}
00252                         digit++; 
00253                         sprintf(cdig,"%1d",digit);
00254                         xs[first_non_nine-start]=cdig[0]; 
00255 //printf("digit is now %d ; cdig is now %s ; xs is >>%s<<\n",digit,cdig,xs);
00256                         /// Change other nines to zeros
00257                         for(i=(first_non_nine+1);i<(len+start);i++){
00258 //printf("i is %d ; num[i] is %c\n",i,num[i]);
00259                                 switch (num[i]) {
00260                                         case '9': xs[i-start]='0';
00261 //printf("\t xs[1+i] is %c\n",xs[i+1]);
00262                                                 break;
00263                                         case '.': xs[i-start]='.';
00264 //printf("\t xs[1+i] is %c\n",xs[i+1]);
00265                                                 break;
00266                                         default: mywhine("get_float_string: Problem with coding in all nines switch.");
00267                                         }
00268                                 }
00269                         xs[len]='\0';///< Ensure null termination
00270                         }
00271         }
00272 else{ ///< if that situation isn't present
00273 /*printf("num[checkpt] (else) is %c\n",num[checkpt]);*/
00274         switch(num[checkpt]){
00275                 case '9':       
00276                 case '8':
00277                 case '7':
00278                 case '6':
00279                 case '5':       // round up the last number
00280                         for(i=0;i<len-1;i++){xs[i]=num[start+i];} ///< Copy as usual up to the end-1 space
00281                         /// Increment the "end-1" digit 
00282                         cdig[0]=num[start+len-1];
00283 //printf("cdig is >>%s<<\n",cdig);
00284                         digit=atoi(cdig); 
00285 //printf("digit is >>%d<<\n",digit);
00286                         if((digit<0)||(digit>8)){mywhine("get_float_string: Digit outside range of 0-8 but shouldn't be.");}
00287                 digit++; 
00288 //printf("digit is now >>%d<<\n",digit);
00289                         sprintf(cdig,"%1d",digit);
00290 //printf("cdig is now >>%s<<\n",cdig);
00291                         xs[len-1]=cdig[0]; 
00292                         xs[len]='\0'; ///< Ensure null termination
00293                         break;
00294                 default: // numbers 4 and lower and also the null terminator
00295                         for(i=0;i<len;i++){xs[i]=num[start+i];} ///< Copy as usual up to the end space
00296                         xs[len]='\0'; ///< Ensure termination.
00297                         break;
00298                 }
00299         }
00300         
00301 // Since clear allocating, we don't need to worry about the final '\0', but might anyway
00302 s=(char*)calloc(w,sizeof(char)); ///< Allocate memory for new string
00303 
00304 k=w-len; ///< Calculated the padding needed (if any)
00305 /*printf("len is %d and strlen(xs) is %d [w=%d ; d=%d]\n",len,strlen(xs),w,d);*/
00306 if(k<0){mywhine("get_float_string: w less than len, but it shouldn't be.");}
00307 switch (j) {
00308         case 'L':
00309         case 'l':
00310                 t=(char*)calloc(k+1,sizeof(char));
00311                 for(i=0;i<k;i++){t[i]=' ';} // space padding
00312                 t[k]='\0'; // for safety
00313                 if((strlen(t)+strlen(xs))!=w){mywhine("get_float_string: coding bug in s/t/w string handling, left.");}
00314                 strcat(s,xs);
00315                 strcat(s,t);
00316                 break;
00317         case 'R':
00318         case 'r': 
00319                 t=(char*)calloc(k+1,sizeof(char));
00320                 for(i=0;i<k;i++){t[i]=' ';} // space padding
00321                 t[k]='\0'; // for safety
00322                 if((strlen(t)+strlen(xs))!=w){mywhine("get_float_string: coding bug in s/t/w string handling, right.");}
00323                 strcat(s,t);
00324                 strcat(s,xs);
00325                 break;
00326         default:
00327                 mywhine("get_float_string: unrecognized justification specification");
00328                 break;
00329         }
00330 free(t);
00331 free(xs);
00332 s[w]='\0'; // for safety
00333 return s;
00334 }
00335 
00336 
00337 //START HERE -- rewrite this for exponential notation
00338 /* */
00339 /************* get_exp_string() ***************/
00340 const char * get_exp_string(double x, char j, int w, int d){
00341 
00342 printf("\n\nThe function get_exp_string hasn't been written yet.  It's very close, though!\n");
00343 printf("Perhaps you might consider writing it?  The basic template is in place... \n");
00344 printf("Essentially, you just need to alter the get_float_string function.\n");
00345 printf("Unfortunately, however, at the time, this program will have to exit.\n");
00346 printf("Bye!\n\n");
00347 exit(0);
00348 
00349 return "hello, world";
00350 }
00351 /*
00352 const char * get_exp_string(double x, char j, int w, int d){
00353 char *s,*t,*xs,*num[151],cdig[2];
00354 int i=0,k=0,tst=0,len=0,start=0,digit=0;
00355 
00356 if(w>150){mywhine("In get_float_string: Cannot have total width greater than 150 characters.");}
00357 if(d>74){mywhine("In get_float_string: Cannot have number of decimal places greater than 74.");}
00358 cdig[0]=cdig[1]='\0';
00359 
00360 tst=log10(x)+1;
00361 k=w-tst; // difference between outgoing length and length of integer part of float value
00362 /// If the incoming string (x) is longer than outgoing size (w),
00363 ///     write something to stderr and print what fits
00364 if(k<0){
00365         fprintf(stderr,"\nget_float_string: WARNING!!!  Integer portion of incoming value exceeds requested outgoing width.\n");
00366         fprintf(stderr,"\tReturning non-numeric string. \n\n");
00367         s=(char*)calloc((w+1),sizeof(char)); ///< Allocate memory for new string
00368         for(i=0;i<w;i++){s[i]='*';}
00369         return s;
00370         }
00371 */
00372 /** Otherwise, print a really wide version of the incoming number to a really wide string
00373   Here, the string is 150 chars wide, and 74 of those chars are decimal digits.
00374   Another 74 are allocated for the integer part of the float representation.
00375   That leaves two spaces, one for a decimal point and another for '-' if needed. 
00376   At present, this function will not return a '+' sign for positive values. */
00377 /*
00378 sprintf(num,"%150.74f",x): ///< 150 wide, minus +/- designation and decimal point and half of that for the fraction
00379 len=tst+d+3; ///< tst=integer part ; d=fraction part ; 3 = space for \0, . & sign
00380 if(d==0){len=tst+2;} ///< tst=integer part ; 2 = space for \0 and sign.
00381 if(w<len){ //< print a statement if we have to give back fewer decimal places to fit the width, but do it anyway
00382         fprintf(stdout,"\nget_float_string: Float width including requested decimal places exceeds requested outgoing width.\n\tRounding.  Hope that's ok.\n\n");
00383         len=w;}
00384 
00385 start=75-tst-1; ///< the starting point in the big number 
00386 if(start<0){fprintf(stderr,"get_float_string: WARNING!! Incoming number too large to round up.\n");
00387         fprintf(stderr,"\tReturning non-numeric string.\n\n");
00388         s=(char*)calloc((w+1),sizeof(char)); ///< Allocate memory for new string
00389         for(i=0;i<w;i++){s[i]='*';}
00390         return s; // return the bad s and stop bothering
00391         }
00392 //if(start<0){mywhine("get_float_string: Problem in first assignment of start value.");}
00393 end=start+len+1; ///< the ending point in the big number
00394 if(end>=150){mywhine("get_float_string: Problem in first assignment of end value.");}
00395 
00396 checkpt=end+1; ///< check the integer where the null character will finally go
00397 if((checkpt<150)&&(num[checkpt]=='.')){checkpt++;} ///< if there is a decimal point there, move over by one, 
00398                 ///< but only if that won't put us over 150 chars (shouldn't ever happen, really) 
00399 xs=(char*)calloc((len+1),sizeof(char)); ///< Allocate memory for new string
00400 switch(num[checkpt]){
00401         case '9':       // do stuff for having found a nine
00402                 // Check to the left until a non-nine digit is found
00403                 for(first_non_nine=checkpt-1;first_non_nine>=start;first_non_nine--){if((num[first_non_nine]!='9')&&(num[first_non_nine]!='.')){break;}}
00404                 if((num[first_non_nine]=='.')||(num[first_non_nine]=='9')){mywhine("get_float_string: Problem with coding in first non-nine.");}
00405                 if(first_non_nine==start){// If the first space:
00406                         xs[0]=num[start];///< Print the first character (space or '-') 
00407                         xs[1]='1';///< Print a 1 in the next space
00408                         /// For the rest of the string-1, for every 9 add a zero (one space over).  Copy any decimals as-is
00409                         for(i=1;i<len;i++){
00410                                 switch (num[start+i]) {
00411                                         '9': xs[i+1]=0;
00412                                                 break;
00413                                         '.': xs[i+1]='.';
00414                                                 break;
00415                                         default: mywhine("get_float_string: Problem with coding in all nines switch.");
00416                                         }
00417                                 }
00418                         xs[len]='\0';///< Ensure null termination
00419                         }
00420                 else{
00421                         for(i=0;i<first_non_nine;i++){xs[i]=num[start+i];} ///< Copy as usual up to "first_not_nine"
00422                         /// Increment the "first_not_nine" digit 
00423                         cdig[0]=num[first_non_nine];
00424                         digit=atoi(cdig); 
00425                         if((digit<0)||(digit>8)){mywhine("get_float_string: Digit outside range of 0-8 but shouldn't be.");}
00426                         digit++; 
00427                         sprintf(cdig,"%1d",digit);
00428                         xs[first_non_nine]=cdig[0]; 
00429                         /// Change other nines to zeros
00430                         for(i=(first_non_nine+1);i<len;i++){
00431                                 switch (num[start+i]) {
00432                                         '9': xs[i]=0;
00433                                                 break;
00434                                         '.': xs[i]='.';
00435                                                 break;
00436                                         default: mywhine("get_float_string: Problem with coding in all nines switch.");
00437                                         }
00438                                 }
00439                         xs[len]='\0';///< Ensure null termination
00440                         }
00441                 break;
00442         case '8':
00443         case '7':
00444         case '6':
00445         case '5':       // round up the last number
00446                 for(i=0;i<len-1;i++){xs[i]=num[start+i];} ///< Copy as usual up to the end-1 space
00447                 /// Increment the "end-1" digit 
00448                 cdig[0]=num[len-1];
00449                 digit=atoi(cdig); 
00450                 if((digit<0)||(digit>8)){mywhine("get_float_string: Digit outside range of 0-8 but shouldn't be.");}
00451                 digit++; 
00452                 sprintf(cdig,"%1d",digit);
00453                 xs[len-1]=cdig[0]; 
00454                 xs[len]='\0'; ///< Ensure null termination
00455                 break;
00456         default: // numbers 4 and lower and also the null terminator
00457                 for(i=0;i<len;i++){xs[i]=num[start+i];} ///< Copy as usual up to the end space
00458                 xs[len]='\0'; ///< Ensure termination.
00459                 break;
00460         }
00461 
00462 // Since clear allocating, we don't need to worry about the final '\0', but might anyway
00463 s=(char*)calloc(w,sizeof(char)); ///< Allocate memory for new string
00464 
00465 k=w-len; ///< Calculated the padding needed (if any)
00466 if(k<0){mywhine("get_float_string: w less than len, but it shouldn't be.");}
00467 switch (j) {
00468         case 'L':
00469         case 'l':
00470                 t=(char*)calloc(k+1,sizeof(char));
00471                 for(i=0;i<k;i++){t[i]=' ';} // space padding
00472                 t[k]='\0'; // for safety
00473                 if((strlen(t)+strlen(x))!=w){mywhine("get_float_string: coding bug in s/t/w string handling, left.");}
00474                 strcat(s,x);
00475                 strcat(s,t);
00476                 break;
00477         case 'R':
00478         case 'r': 
00479                 t=(char*)calloc(k+1,sizeof(char));
00480                 for(i=0;i<k;i++){t[i]=' ';} // space padding
00481                 t[k]='\0'; // for safety
00482                 if((strlen(t)+strlen(x))!=w){mywhine("get_float_string: coding bug in s/t/w string handling, right.");}
00483                 strcat(s,t);
00484                 strcat(s,x);
00485                 break;
00486         default:
00487                 mywhine(stderr,"get_float_string: unrecognized justification specification");
00488                 break;
00489         }
00490 
00491 s[w]='\0'; // for safety
00492 return s;
00493 }
00494 */
 All Classes Files Functions Variables Typedefs Defines