Tuesday, April 5, 2011

How to print out dash or dot using fprintf/printf?

As of now I'm using below line to print with out dot's

fprintf( stdout, "%-40s[%d]", tag, data);

I'm expecting the output would be something like following,

Number of cards..................................[500]
Fixed prize amount [in whole dollars]............[10]
Is this a high winner prize?.....................[yes]

How to print out dash or dot using fprintf/printf?

From stackoverflow
  • You are going to have to output the string with the dot or dash padding yourself.

    Something like (forgive my C, it's rusty):

    printAmount(char *txt, int amt) {
        printf("%s",txt);
        for(int xa=strlen(txt); xa<40; xa++) { putc('.'); }
        printf("[%d]",amt);
        printf("\n");
        }
    
  • You can't do it in one statement. You can use sprintf, then substitute dots for spaces yourself, or do something like

    int chars_so_far;
    char padder[40+1]= '..........'; //assume this is 40 dots.
    printf("%.40s%n",tag,&chars_so_far);
    printf("%s[%d]",padder+chars_so_far,data);
    


    Edit: Simplified my example above based on @Ates' padder concept. This way doesn't require any 'leaps of faith', about whether the tag string is too big or too small - it always starts the data in column 41.

    Ates Goral : I think it can be done in one statement.
    Christoph : @Ates: it could, but imo not without two calls to `strlen()` if you want to properly check boundaries - therefore, you need to cache this value -> two statements!
  • I'd suggest writing a function that pads a string with X characters and use that to generate the first argument to your printf string. Something like:

    char s[40];
    pad_str(tag, s, 40, '.');
    fprintf( stdout, "%-40s[%d]", s, data);
    

    Note that the third line of your sample data would need this format:

    "%-40s[%s]"
    
  • A faster approach:

    If the maximum amount of padding that you'll ever need is known in advance (which is normally the case when you're formatting a fixed-width table like the one you have), you can use a static "padder" string and just grab a chunk out of it. This will be faster than calling printf or cout in a loop.

    static const char padder[] = "......................"; // Many chars
    
    size_t title_len = strlen(title);
    size_t pad_amount = sizeof(padder) - 1 - title_len;
    
    printf(title); // Output title
    
    if (pad_amount > 0) {
        printf(padder + title_len); // Chop!
    }
    
    printf("[%d]", data);
    

    You could even do it in one statement, with some leap of faith:

    printf("%s%s[%d]", title, padder + strlen(title), data);
    
    Christoph : your last suggestion breaks if `strlen(title) > strlen(padder)`
    Ates Goral : @Christoph, hence the proclaimed "leap of faith" :)
    Christoph : @Ates: interesting concept, this 'faithful programming' of yours ;)
  • Another solution using a tiny helper function

    static inline size_t min(size_t a, size_t b)
    {
        return a < b ? a : b;
    }
    

    Then, you can do the following:

    char padder[] = "........................................";
    int len = min(strlen(tag), sizeof(padder) - 1);
    printf("%.*s%s[%d]", len, tag, padder + len, data);
    

    This is essentially what Ates posted, but I actually figured this out on my own ;)

    Jonathan Leffler : Change the size_t to int; you must pass an int to the '*' in the printf() family of functions.
    Ates Goral : Or make the helper more helpful: printf("%s%s[%d]", title, getPadding(title), data);
    Christoph : @Jonathan: thanks, corrected it...
  • You can easily do this with iostreams instead of printf

    cout << setw(40) << setfill('.') << left << tag[i] << '[' << data[i] << ']' << endl;
    

    Or if you really need to use fprintf (say, you are passed a FILE* to write to)

    strstream str;
    str << setw(40) << setfill('.') << left << tag[i] << '[' << data[i] << ']' << endl;
    printf(%s", str.str());
    
    Nick Presta : +1 for being easy to read and understand and not breaking under certain (albeit somewhat uncommon situations).
  • I think there's a better way.

    #include <string.h>
    #include <stdio.h>
    
    #define MIN(A,B) ((A)<(B)?(A):(B))
    char *dashpad(char *buf, int len, const char *instr) {
        memset(buf, '-', len);
        buf[len] = 0;
        int inlen = strlen(instr);
        memcpy(buf, instr, MIN(len, inlen));
        return buf;
    }
    main() {
        char buf[40];
        printf("%s\n", dashpad(buf, 40, "Hello world, dash padded "));
    }
    
    Ates Goral : First filling the entire buffer with the pad character, and then overwriting them seems a bit wasteful though...
    Software Monkey : Also, you have a buffer overrun (don't you love C) - s/be: buf[len-1]=0;
    Software Monkey : And you have a null-erasure bug in you min(), which should also be len-1.
    Thi : is it possible to call a function inside printf?
    Software Monkey : @Thi: Yes this is legitimate - the function is invoked and it's return passed as a parameter to printf... this is normal coding in any procedural, OOP or functional language.

0 comments:

Post a Comment