使用printf,左右对齐给定长度的两个字符串

时间:2012-09-28 10:46:04

标签: c bash printf justify text-justify

在C或Bash shell中使用printf,如何将两个字符串(字符数组)左右对齐到给定长度?

E.g。如果字符串是“堆栈”和“溢出”,并且长度是20个字符,我希望打印

stack-------overflow

(为清楚起见,每个空格都显示为破折号)。

字符串长度未知。如果总长度+ 1超过给定的长度,是否可以从给定方向截断一个或两个字符串并在它们之间留一个空格?例如。如果长度是10,我们能得到这些吗?

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

我知道printf(“%10s”,string)证明右边有一个字符串而printf(“% - 10s”,字符串)证明左边有一个字符串,但找不到证明2个字符串对齐的方法,或截断它们。

4 个答案:

答案 0 :(得分:3)

这比电池更长,但是它可以更好地分割字符串。此外,它尽可能使用printf进行截断,仅在左手截断第二个参数时回退到其他机制。

击:

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;
}

示例输出:

$ 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'

免责声明:算术可能会溢出,在这种情况下输出将是错误的(或者,如果您可以将strlen(a)+ strlen(b)安排为2 ^ 64字节,则程序将为SIG_FPE)。如果有人关心,我可以为adj_a和adj_b计算提供解释。

答案 1 :(得分:2)

你必须编写自己的函数才能做到这一点。这是一个没有的例子和一个容易截断的例子。对于截断,您需要定义一些基于截断的规则。

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

void concat(const char* str1, const char* str2, char *result, int len)
{
   int str1len = strlen(str1);
   int str2len = strlen(str2);
   memset(result, ' ', len);
   if(!(str1len + str2len > len))
   {
      memcpy(result, str1, str1len);
      memcpy(result + len - str2len, str2, str2len);
      result[len] = '\0';
   }
   else
   {
      memcpy(result, str1, 1);
      memcpy(result + len - str2len, str2, str2len);
      result[len] = '\0';
   }
}

int main()
{
   char str1[] = "stack";
   char str2[] = "overflow";
   int len = 20;
   char* result = malloc(len + 1);
   int len2 = 10;
   char* result2 = malloc(len2 + 1);

   concat(str1, str2, result, len);

   printf("%s\n", result);

   concat(str1, str2, result2, len2);

   printf("%s\n", result2);

   free(result);
   free(result2);

   return 1;
}

答案 2 :(得分:1)

我认为你不能在一次操作中完成它,尤其是截断部分。请注意,例如{C}中的printf("%2s", "hello");截断。字段宽度仅适用于填充目的,而不是截断。

您可能需要实现一个自定义函数来执行此操作,如C:

中所示
void format_pair(char *out, size_t out_max, const char *s1, const char *s2);

答案 3 :(得分:1)

在Bash中:

#!/bin/bash

function justify_two_strings {
    a=$1; shift;
    b=$1; shift;
    len=$1; shift;

    while [[ $(( ${#a} + ${#b} + 1 )) -gt $len ]]; do
        a=${a:0:-1}     # cut the last character of $a
        b=${b:1}        # cut the first character of $b
    done

    printf "%s%$(( len-${#a} ))s\n" $a $b
}

justify_two_strings 'stack' 'overflow' 20   # prints 'stack       overflow'
justify_two_strings 'stack' 'overflow' 10   # prints 'sta erflow'

如果字符串不合适,您可能需要调整while循环的核心以缩短字符串。

一些提示:

  • ${#a}给出了$a
  • 的长度
  • ${a:s:n}n开始返回$a个字符,从索引s开始(从0开始)。如果省略n,则返回所有内容直到字符串结尾。