Home > Blockchain >  How to display a string with a regular size and filled with other characters than space using printf
How to display a string with a regular size and filled with other characters than space using printf

Time:09-13

I know that we can display a given string using printf() defining its minimum size. This can be done in this way, for example:

printf(" s",str);

However, with this piece of code, str will be displayed with a minimum of 20 characters and, if the original string has less than 20 characters, the function completes with spaces at left.

Is there any way of doing this using other characters than white spaces?

CodePudding user response:

if the original string has less than 20 characters, the function completes with spaces at left.

Is there any way of doing this using other characters than white spaces?

No, not with printf() alone. The documentation (for example, https://man7.org/linux/man-pages/man3/printf.3.html) specifically says that if the converted value has fewer characters than the field width, then the field is padded with spaces on the left or right, as appropriate.

The language spec is actually a little less clear on this point than many other sources:

If the converted value has fewer characters than the field width, it is padded with spaces (by default) on the left (or right, if the left adjustment flag, described later, has been given) to the field width.

One might take the "by default" to indicate that another choice of padding character could somehow be specified, but careful reading of the specification does not reveal any such option. The best interpretation, then, is that the "by default" is referring to the location of the padding (left), not the padding character. Most other versions of the doc just leave out the "by default" bit, presumably because it is more confusing than helpful.

CodePudding user response:

No, the printf family has no ability to use arbitrary padding characters.

One possibility would be to use memset to pre-fill a buffer with the padding character, then use sprintf to print the result into part of it:

char *string = "Hello, world!";
int stringlen = strlen(string);
int fieldlen = 20;
char padchar = '#';

if(stringlen <= fieldlen) {
    char buf[fieldlen 1];
    memset(buf, padchar, fieldlen);
    sprintf(&buf[fieldlen - stringlen], "%s", string);
    printf("%s\n", buf);
}

CodePudding user response:

Is there any way of doing this using other characters than white spaces?

Yes, if that pad character is '0' and at least 1 needed. Use arithmetic padding.

#define N 20
size_t len = strlen(str);
assert(len < N);
//       v------- Pad with 0
//       |v------ Obtain width from arguments
printf("%0*d%s",(int)(N-len), 0, str);

We can add code for alternate padding. Good for len [0...N].

size_t len = strlen(str);
#define N 20
assert(len <= N);
char pad[N 1];
memset(pad, pad_char, N-len);
pad[len] = 0;
printf("%s%s",pad, str);

CodePudding user response:

I might solve it like this

/* ... mystery content removed ... */

int main(int argc, char **argv)
{
  for (int i = 1; i < argc; i  )
    printf("Left: " PAD_STAR_FMT "\n", PAD_STAR_ARG(argv[i], 20));

  for (int i = 1; i < argc; i  )
    printf("Rite: " PADR_STAR_FMT "\n", PADR_STAR_ARG(argv[i], 20));

  return 0;
}

Run:

$ ./pad The quick brown fox "" "supercalifragilisticexpeali"
Left: *****************The
Left: ***************quick
Left: ***************brown
Left: *****************fox
Left: ********************
Left: supercalifragilistic
Rite: The*****************
Rite: quick***************
Rite: brown***************
Rite: fox*****************
Rite: ********************
Rite: supercalifragilistic

The PAD_STAR_FMT and PADR_STAR_FMT macros expands to a string literal which provides the conversion specifier for the star-padded print job.

The PAD_STAR_ARG and PADR_STAR_ARG macros expand to the argument to this conversion specifier.

As you can see, we encode the field width of 20 into the argument space rather than the format string because we need to be able to do some calculations with it, not possible in the format string language.

Under the hood, two conversion specifiers are used, and the ARG macros generate several arguments for them (unfortunately, with multiple evaluations of their own arguments).

The nice thing about this approach is that we can retrofit it into some existing code that already uses printf without otherwise disturbing that code. If the code makes a single printf call, we can keep it that way.

I recommend cleaning up the code and using better, perhaps shorter names. My if statements could be ternary operators, too.

The complete program follows:

#include <stdio.h>
#include <string.h>
#include <assert.h>

static char stars[] = {
  "**************************************************"
  "**************************************************"
  "**************************************************"
  "**************************************************"
  "**************************************************"
  "**************************************************"
  "**************************************************"
  "**************************************************"
  "**************************************************"
  "**************************************************"
};

#define PAD_STAR_FMT "%.*s%.*s"
#define PAD_STAR_ARG(S, N) pad_nstars(S, N), stars, pad_nchars(S, N), (S)

#define PADR_STAR_FMT "%.*s%.*s"
#define PADR_STAR_ARG(S, N) pad_nchars(S, N), (S), pad_nstars(S, N), stars

static int pad_nstars(const char *s, size_t width)
{
  assert (width < sizeof stars);
  size_t len = strlen(s);
  if (len > width)
    return 0;
  return (int) (width - len);
}

static int pad_nchars(const char *s, size_t width)
{
  size_t len = strlen(s);
  if (len > width)
    return width;
  return len;
}

int main(int argc, char **argv)
{
  for (int i = 1; i < argc; i  ) {
    printf("Left: " PAD_STAR_FMT "\n", PAD_STAR_ARG(argv[i], 20));
  }

  for (int i = 1; i < argc; i  ) {
    printf("Rite: " PADR_STAR_FMT "\n", PADR_STAR_ARG(argv[i], 20));
  }
  return 0;
}
  • Related