GLYLIB
0.3.0b
|
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 */