bash 使用 printf,将两个字符串左对齐和右对齐到给定长度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12638520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 03:24:42  来源:igfitidea点击:

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

cbashprintfjustifytext-justify

提问by Gnubie

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

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

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

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

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?

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

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.

我知道 printf("%10s", string) 将一个字符串向右对齐, printf("%-10s", string) 将一个字符串向左对齐,但找不到一种方法来对齐 2 个字符串,或者截断它们。

采纳答案by rici

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.

这比电池长,但恕我直言,它可以更好地分开琴弦。此外,它在可能的情况下使用 printf 进行截断,仅在第二个参数的左侧截断时回退到其他机制。

Bash:

重击:

truncat () {
  local len= a= b= 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:

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.

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

回答by Minion91

You will have to write your own function to do that. Here is an example without and one with an easy truncation. For the truncation you will need to define some rules to base the truncation on.

您必须编写自己的函数才能做到这一点。这是一个没有简单截断的示例。对于截断,您需要定义一些规则来作为截断的基础。

#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] = '
#!/bin/bash

function justify_two_strings {
    a=; shift;
    b=; shift;
    len=; 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'
'; } else { memcpy(result, str1, 1); memcpy(result + len - str2len, str2, str2len); result[len] = '
void format_pair(char *out, size_t out_max, const char *s1, const char *s2);
'; } } 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; }

回答by battery

In Bash:

在 Bash 中:

##代码##

You might want to tweak the core of whileloop to shorten the strings differently if they do not fit.

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

Some hints:

一些提示:

  • ${#a}gives the length of $a
  • ${a:s:n}returns ncharacters from $astarting at index s(0-based). If nis omitted, then it returns everything until the end of the string.
  • ${#a}给出长度 $a
  • ${a:s:n}返回n$a索引开始的字符(从s0开始)。如果n省略,则返回直到字符串结束的所有内容。

回答by unwind

I don't think you can do it in a single operation, especially the truncating part. Note that e.g. printf("%2s", "hello");will nottruncate, in C. The field width only matters for padding purposes, not truncating.

我不认为您可以在一次操作中完成它,尤其是截断部分。请注意,如printf("%2s", "hello");不会截断,C中的字段宽度仅事项填充的目的,而不是截断。

You will probably need to implement a custom function to do this, something like this in C:

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

##代码##