5

C または Bash シェルで printf を使用して、2 つの文字列 (文字配列) を特定の長さに左寄せおよび右寄せするにはどうすればよいですか?

たとえば、文字列が「stack」と「overflow」で、長さが 20 文字の場合、印刷したい

stack-------overflow

(わかりやすくするために、各スペースはダッシュとして示されています)。

文字列の長さは不明です。合計の長さ + 1 が指定された長さを超える場合、いずれかの方向から 1 つまたは両方の文字列を切り捨て、それらの間にスペースを残すことは可能ですか? たとえば、長さが 10 の場合、これらのいずれかを取得できますか?

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

printf("%10s", string) は 1 つの文字列を右に揃え、printf("%-10s", string) は 1 つの文字列を左に揃えることを知っていますが、2 つの文字列を揃える方法が見つかりません。それらを切り捨てます。

4

4 に答える 4

3

これはバッテリーよりも長いですが、それは文字列をよりよく分割します。また、可能な場合はprintfを使用して切り捨てを行い、2番目の引数の左側の切り捨てに対してのみ他のメカニズムにフォールバックします。

バッシュ:

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の計算について説明することができます。

于 2012-09-29T01:36:18.263 に答える
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;
}
于 2012-09-28T11:03:30.887 に答える
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}インデックス(0ベース)nから文字を返します。省略した場合は、文字列の最後まですべてを返します。$asn
于 2012-09-28T11:12:45.160 に答える
1

特に切り捨て部分は、1回の操作でできるとは思いません。たとえば、C では切り捨てられないprintf("%2s", "hello");ことに注意してください。フィールド幅はパディングの目的でのみ重要であり、切り捨てではありません。

これを行うには、おそらく C で次のようなカスタム関数を実装する必要があります。

void format_pair(char *out, size_t out_max, const char *s1, const char *s2);
于 2012-09-28T10:54:21.917 に答える