Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using printf, left- and right-justify two strings to a given length

Using printf in C or Bash shell, how can I left- and right-justify two strings (character arrays) to a given length?

E.g. if the strings are "stack" and "overflow", and the length is 20 characters, I wish to print

stack-------overflow

(for clarity, each space is shown as a dash).

The strings are of unknown length. If the total length + 1 exceeds the length given, is it possible to truncate one or both strings from either given direction and leave a space between them? E.g. if length is 10, can we get any of these?

stack-over
stack-flow
s-overflow
k-overflow

I know that printf("%10s", string) justifies one string to the right and printf("%-10s", string) justifies one string to the left, but can't find a way to justify 2 strings, or to truncate them.

like image 835
Gnubie Avatar asked Sep 28 '12 10:09

Gnubie


People also ask

How do I left justify using printf?

Use %S as the printf String specifier to output upper-case text. Precede the letter s with a number to specify field width. Put a negative sign after the % to left-justify the text.

How do I printf a string?

We can print the string using %s format specifier in printf function. It will print the string from the given starting address to the null '\0' character. String name itself the starting address of the string. So, if we give string name it will print the entire string.

Is used to specify width for string?

setw function is a C++ manipulator which stands for set width. The manipulator sets the ios library field width or specifies the minimum number of character positions a variable will consume. In simple terms, the setw C++ function helps set the field width used for output operations.


1 Answers

This is longer than battery's, but imho it splits the strings better. Also it uses printf for truncation where possible, falling back to other mechanisms only for left-hand truncation of the second argument.

Bash:

truncat () {
  local len=$1 a=$2 b=$3 len_a=${#2} len_b=${#3}
  if ((len <= 0)); then return
  elif ((${len_b} == 0)); then
    printf %-${len}.${len}s "$a"
  elif ((${len_a} == 0)); then
    printf %${len}.${len}s "${b: -$((len<len_b?len:len_b))}"
  elif ((len <= 2)); then
    printf %.${len}s "${a::1}${b: -1}"
  else
    local adj_a=$(((len_a*len+len_b-len_a)/(len_a+len_b)))
    local adj_b=$(((len_b*len+len_a-len_b-1)/(len_a+len_b)))
    printf "%-${adj_a}.${adj_a}s %${adj_b}.${adj_b}s" \
           "$a" \
           "${b: -$((len_b<adj_b?len_b:adj_b))}"
  fi
}

C:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

void truncat(long len, const char* a, const char* b) {
  if (len <= 0) return;
  unsigned long long len_a = strlen(a);
  unsigned long long len_b = strlen(b);
  if (!len_b)
    printf("%-*.*s", (int)len, (int)len, a);
  else if (!len_a)
    printf("%*s", (int)len, b + (len < len_b ? len_b - len : 0));
  else if (len <= 2)
    printf("%.1s%.*s", a, (int)(len - 1), b + len_b - 1);
  else {
    unsigned long long adj_a = (len_a * len + len_b - len_a) / (len_a + len_b);
    unsigned long long adj_b = (len_b * len + len_a - len_b - 1) / (len_a + len_b);
    printf("%-*.*s %*s",
           (int)adj_a, (int)adj_a, a,
           (int)adj_b, b + (adj_b < len_b ? len_b - adj_b : 0));
  }
}

int main(int argc, char** argv) {
  truncat(atol(argv[1]), argv[2], argv[3]);
  return 0;
}

Sample output:

$ for i in {0..20}; do printf "%2d '%s'\n" $i "$(./truncat $i stack overflow)"; done
 0 ''
 1 's'
 2 'sw'
 3 's w'
 4 's ow'
 5 'st ow'
 6 'st low'
 7 'st flow'
 8 'sta flow'
 9 'sta rflow'
10 'stac rflow'
11 'stac erflow'
12 'stac verflow'
13 'stack verflow'
14 'stack overflow'
15 'stack  overflow'
16 'stack   overflow'
17 'stack    overflow'
18 'stack     overflow'
19 'stack      overflow'
20 'stack       overflow'

Disclaimer: The arithmetic can overflow, in which case the output will be wrong (or, if you can arrange for strlen(a)+strlen(b) to be exactly 2^64 bytes, the program will SIG_FPE). I can provide an explanation for the adj_a and adj_b computations if anyone cares.

like image 180
rici Avatar answered Nov 15 '22 04:11

rici