Link to home
Start Free TrialLog in
Avatar of lblinc
lblincFlag for United States of America

asked on

C --> how to apply a comma format to any column, or multiple columns of either int OR float data

Guys,  I was only able to get my "Commify" function to work on 1 column at a time,  thus only 1 column in the print statement could be "commify'd".   Admit that i am lost with this one as i need to get more experience programming C using buffers, etc.    If anyone has some thoughts in the meantime, here is some example data and the code I'm using below...  using Kent's advice I started to add buffers to the beginning of the insert_commas() function, but still unsure how to put this all together..  ?    - LBL (I will make this more points b/c i really would like to resolve this one!)

Data Before -->  (would like to Commify cols 2,  5,  6,  7 here...)
AAA    1000000    10.000    1.000    1000000    1000000    1000000
BBB    2000000    20.000    2.000    2000000    2000000    2000000
CCC    3000000    30.000    3.0000    3000000    3000000    3000000

Data After  -->  (Requested Output should look like here...)
AAA    1,000,000    10.000    1.000    1,000,000    1,000,000    1,000,000
BBB    2,000,000    20.000    2.000    2,000,000    2,000,000    2,000,000
CCC    3,000,000    30.000    3.0000    3,000,000    3,000,000    3,000,000

The print statement -->
(void)printf("%-7s%7.0f%8.3f%8.3f %10.0f %8.0f %9.0f\n", col1, col2, col3, col4, col5, col6, col7);      //  trying to commify col2, col5, col6, col7


char *CommifyInt(long *Value)
{
      int orig_value;
      int abs_value;
      char sibuf[512];
      char sibuf2[512];

      orig_value = *Value;
      abs_value = abs(orig_value);
      sprintf( sibuf, "%ld", abs_value );
      insert_commas(sibuf);
      sprintf( sibuf2, "%s%s", orig_value < 0 ? "-" : "", sibuf );
      return sibuf2;
}


char *CommifyFloat(double *Value)
{
      int orig_value;
      int abs_value;
      char sfbuf[512];
      char sfbuf2[512];

      orig_value = *Value;
      abs_value = abs(orig_value);
      sprintf( sfbuf, "%lf", abs_value );
      insert_commas(sfbuf);
      sprintf( sfbuf2, "%s%s", orig_value < 0 ? "-" : "", sfbuf );
      return sfbuf2;
}

void
*insert_commas(char *s1)
{
  char *s2,*s3, *s5;
  static char b0[15];
  static char b1[15];
  static char b2[15];
  static char b3[15];
  static char b4[15];
  static char b5[15];
  static char b6[15];
  static char b7[15];
  static char *bx[8] = {&b0, &b1, &b2, &b3, &b4, &b5, &b6, &b7};
  static int BufferCount = 0;

  s5 = bx[(BufferCount++) & 7];

  s3 = strchr(s1,'.');
  if(s3 == NULL) {
    reverse(s1);
    doit(s1);
    reverse(s1);
  } else {
    s2 = strtok(s1,".");
    s3 = strtok('\0',".");
    strcpy(s5,s3);
    reverse(s2);
    doit(s2);
    reverse(s2);
    sprintf(s1,"%s.%s",s2,s5);
  }
   return s5;
}

void
*reverse(char *s1)
{
  int i, len;      

  if(strlen(s1) < 1)
      return;      

   for (i=0, len=strlen(s1)-1; i<(strlen(s1)/2); i++, len--) {
    char t = s1[i];
      s1[i] = s1[len];
      s1[len] = t;
  }
    return;
}

void
*doit(char *s1)
{
  int len = strlen(s1);
  int i, j;      
  char t1[30];
 
  for(i=0, j=0; i<len; i++) {
        if((!(i % 3)) && (i > 0))
            t1[j++] = ',';
        t1[j++] = s1[i];
  }
  t1[j] = '\0';
  strcpy(s1,t1);
    return;
}
Avatar of harsha_dp
harsha_dp
Flag of India image

Convert value to String ,[  using sprintf  ]
Then do substring operations
     Search for decimal point. say ith position
    Using this i u can decide where commas should be. Just make a new string using commas.

Like 123456.99900   index will be 6,
need to insert comma at (6-3) = 3rd position,
and another (6-5) = 1st position.
 So it would be 1,23,456.999900


     
Avatar of lblinc

ASKER

Kent -  I closed the threads on this last week when I got this working properly, but only on 1 column of data!   I thought I could easily move ahead to the rest of the columns...    but i could only get  1 variable from the printf statement to be comma formatted.  

I do not have ANY questions on how to do the comma formatting on the numbers (i've been shown plenty of examlpes).  

My question remaining, before i give up on this ;), is how to successfully printf multiple columns in this comma format.   I realize it's a simple concept, but I've made so many different attempts with no luck.

Isn't there a brief example of this somewhere?    Should the printf statement look like the following?

Let's say you wanted to comma format float data columns 2, 3, 4 and 7 from below printf...    is this correct to try to use printf this way?

(void)printf("%7s %12s %12s %12s %8.3f %9.5f %12s\n", name, CommaConvertFloat(&data2), CommaConvertFloat(&data3), CommaConvertFloat(&data4), data5, data6, CommaConvertFloat(&data7));

-----------------------------------------------------------------------------------------------------------------------------------

If there was just one working example (not just the comma format piece) where this Data Before could look like this Data After (see below).     Again, the before data in  cols 2,  5,  6,  7 is type  FLOAT because calcs need to be performed before the formatting.    

Data Before -->  (would like to Commify cols 2,  5,  6,  7 here...)
AAA    1000000.00    10.000    1.000    1000000.00    1000000.00    1000000.00
BBB    2000000.00    20.000    2.000    2000000.00    2000000.00    2000000.00
CCC    3000000.00    30.000    3.0000    3000000.00    3000000.00    3000000.00

Data After  -->  (Requested Output should look like here...)
AAA    1,000,000.00    10.000    1.000    1,000,000.00    1,000,000.00    1,000,000.00
BBB    2,000,000.00    20.000    2.000    2,000,000.00    2,000,000.00    2,000,000.00
CCC    3,000,000.00    30.000    3.0000    3,000,000.00    3,000,000.00    3,000,000.00

I know, i know...   All this just to get float data formatted properly.   Not sure if homework will do me any good today, i'm fairly beat up over this.   -  LBL




ASKER CERTIFIED SOLUTION
Avatar of sunnycoder
sunnycoder
Flag of India 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
Avatar of lblinc

ASKER

btw  -   the data I'm working with is read in from a text file and convereted to float type in most cases in order to do the calcs...  

strncpy(buf, str+7+6, 10); buf[10] = 0; data2 = atof(buf);
strncpy(buf, str+7+6+10,13); buf[13] = 0; data5 = atof(buf);
strncpy(buf, str+7+6+10+13, 13); buf[13] = 0; data6 = atof(buf);

after performaing the calcs,  I then can do the comma formatting just fine.  But...

where I'm stuck now is in these 2 areas...

1)   passing the data back to the printf  
2)   running this function on multiple columns of data, all that were of type float

Somebody out there must have had to do a very similar comma format procedure on multiple columns of float numbers?   No?    -  LBL

There are several good techniques.  Here's one that will work no matter how many fields you want to format.

char Buffer[200];
char *Object;

  fgets (Buffer, 200, stdin);

  Object = strtok (Buffer, " ");   /*  note that strtok will write in the source string  */
  fprintf (" %s    ", Buffer);        /*  echo the first column back to the output file  */
  Object = strtok (NULL, " ");
  while (Object)
  {
    /*  insert commas into Ojbect  */
    fprintf (stdout, " %s  ", ConvertedObject);
  }
  fprintf (stdout, "\n");



Kent
Avatar of lblinc

ASKER

Sunnycoder,    Thanks for your reply.    Yes, all i want is to be able to commify the value, but in "multiple" column(s) of my choice.    I am able to develop some code which can get me the value in the desired column.    

I have to step away now for a bit,  but I will look over your comment...  I think that may be what I've been trying to do.   -  LBL
Avatar of lblinc

ASKER

Kent,  Thanks much.   I have to step away from this now.   but later today I'm going to try to get closure on this,   I'll try your technique, and look over sunnycoder's control flow...

I feel like I'm getting closer.   :)

Cool.

When you try it, you'd better modify the loop to be:

  while (Object)
  {
    /*  insert commas into Ojbect  */
    fprintf (stdout, " %s  ", ConvertedObject);
    Object = strtok (NULL, " ");
  }


Otherwise it will run a llooonnnnnngggggggggggggggg time.  :)

Kent
Avatar of zdes
zdes

what about formatting using a single tick modifier? I had to set the locale first explicitly:

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

main()
{
        struct lconv *l;

        setlocale(LC_NUMERIC,"en_US");
        l = localeconv();
        printf("separator: %s\n", l->thousands_sep);
        printf ("%+-'17.2f", 10000000.00);
}

This produces the following output:
separator: ,
+10,000,000.00


tested on Solaris and RedHat.
Avatar of lblinc

ASKER

zdes -   I tried the  setlocale  code from above, but on my PC...  it compiles fine using  cc -o  and .Net

but the output is as follows:

separator:  
      10000000.00

not sure why it seems to not recognize the separator?     i'm using both cygwin and Microsoft VS .Net

?  -  LBL
 
The names of the locales can differ between the platforms. I do not have access to windoze now. I'll try to check it later today.
Avatar of lblinc

ASKER

zdes -  On windoze, I noticed when I change to   english    instead of     en_US     i now see the comma separator as follows:    

separator: ,
+10000000.00

but still lacking format...   ?

thanks,
- LBL
Avatar of lblinc

ASKER

below code on Win XP produces...     no commas..    ?       need to update locale.h ?   -  LBL

separator: ,
+10000000.00
                                         
--------------------------------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

main()
      {
      struct lconv *l;

      setlocale(LC_NUMERIC, "english");
      l = localeconv();
      printf("separator: %s\n", l->thousands_sep);
      printf("%+-17.2f", 10000000.00);
      }
I am looking through MSDN docs. Apparently, the format specs in printf are different and do not include single tick that I saw documented on Unix. I see that another member of the same lconv struct (grouping) has a correct value of 3, which means to put thousands_set every 3 chars going from the decimal point to the left, but I do not know how to trigger this behavior on windows at the moment (lack of POSIX?)

I have not found C solution for Windows without writing custom code to insert comas. There are some on 'net using C++ and STL. PHP and such.

From the docs I see, C code with a single tick format modifier works ok on aix, hpux, solaris, and linux. but not on Windows. I've installed the latest cygwin with gcc 3.3.3, but it too does not take this modifier. So roll back to what Kdo and others suggest. Or format C:, install Linux :-)
Avatar of lblinc

ASKER

Ok.   Thanks zdes.    - LBL
Avatar of lblinc

ASKER

THANKS TO ALL FOR THE POSTS!     I finally got it the way it should look; it works fine.    I KNOW it could probably be made more efficient, look forward to that actually...   So maybe next questions could be about cleaning it up a bit,  but for now the output looks precisely as i wanted.  

In the end I really found  Sunnycoder's post above to be the most useful to get to completion with things.    I used a derivation of Sunnycoder's control flow example above.    Kdo, as always, ...Kent provided some great input.     The tricky part was that only a few columns out of many (not in order) needed to be Commified...  I would have really liked to use the change of locale using LC_NUMERIC that zdes mentioned, but no luck on windows using .Net after much trial and error.     After this project i decided that Cygwin is ok for now, but thinking of installing Linux in the near future.

My biggest issues were:

1)  I had an existing insert_comma()  function that was not perfect, but functional...needs some work to be 100%...   i wanted to keep it, but it was not easy...   i had to pass abs() values converted into string format to the insert_commas().
2)  The file read into the program had columns in no specific priority making it hard to just print out the entire commified string as Sunnycoder suggested, but i adapted finally using that concept.

-  LBL

LBL, thank you for the props.
Avatar of lblinc

ASKER

Guys   -  As I am fairly new on the experts-exchange board...  I did not realize points could be split.    I will have many more questions in future, I'm sure,  so next time, when all answers are good, or even have indirect solution (but correct) answer to the question, I'll divvy up some points to those that answer this way.

Thanks again.

- LBL

That's OK.  We're used to Sunny grabbing all of the points.

;)

Yeah Right ... Cant recollect where I saw this

  Kdo  24192  
  grg99  14188  
  sunnycoder  13932

ummm ... darn ... <scratching head>

;-)


As near as I can tell, you were on holiday until about the 15th.   :)


It's kind of like when the home team lets the visitors jump out to an early lead and then come storming back to win the game.

Except in this case, you may have let one get away.  If I can only hold you at bay for 3 more days.......


Or maybe you were posting what the monthly totals will have to continue to be for either grg99 or me to catch you in a single lifetime.  ;)


You've been quiet this month.  Good to see you back at it.
Kent
>If I can only hold you at bay for 3 more days.......
What do you think is keeping me awake in front of my PC at 12:15 AM ;-)

At the rates of current month, and indications of work pressures, you will be able to catch up with me in 6-9 months ... I am just trying to stretch that to early 2006 ... If you are going to get it, I may as well make you sweat a bit ;-)

>>What do you think is keeping me awake in front of my PC at 12:15 AM ;-)

Actually, I WAS going to ask you that!

>>If you are going to get it, I may as well make you sweat a bit ;-)

You're assuming that I won't decide to acquire a life in this time.  :)


>>>If you are going to get it, I may as well make you sweat a bit ;-)

>You're assuming that I won't decide to acquire a life in this time.  :)

:-( :-( .. dont spoil my fun :-(