challenge 文字列のセンタリング

文字列を指定のカラム幅にセンタリング配置する関数を示してください。文字列の長さが指定した幅より長い場合には文字列の両端をできるだけ均等に切り落して指定幅に収めてください。1文字は1カラムに収まるものと仮定してかまいません。

Posted feedbacks - C

特にひねりはありません。さらに簡単にできそうな気がします。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *center( char *str, int width, char *out ){
   int len,margin,i=0;
   char *p = out;
   if( !out ){
      return NULL;
   }
   len = strlen(str);
   margin = (abs(width-len)+1)/2;
   if( margin == 0 || len > width ){
      strncpy( p, &str[margin], width );
   }
   else{
      while( i++ < margin ) *p++ = ' ';
      strcpy( p, str );
      p += len;
      while( i++ < width - len + 1 ) *p++ = ' ';
   }
   return out;
}

int main ( int argc, char *argv[] ){
   int n;
   char *out;
   if( argc < 3 ){
      fprintf(stderr, "usage: %s str num\n", argv[0]);
      return EXIT_FAILURE;
   }

   n = atoi( argv[2] );
   if( n <= 0 || (out = malloc( sizeof(char)*n )) == NULL ){
      return EXIT_FAILURE;
   }
   printf("%s#\n", center( argv[1], n, out ) );
   free(out);
   return EXIT_SUCCESS;
}

 まず指定されたカラム幅分をスペースで埋め、さらにヌル文字をつけて作業エリアを初期化します。
 次に幅から文字列長を引いて2で割り、初めの文字のオフセットを求めます。このとき幅より大きな文字列が渡されるとマイナスになりますが、そのままにしておきます。
 最後にオフセットの位置から順番にコピーしますが、作業エリア範囲外の場合は無視します。すると文字列長に関係なく欲しい結果が得られます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <string.h>

#define SPACE ' '
//#define SPACE '*'

char *centering(char *out, const char *str, int width){
    int i, len, offset;
    len = strlen(str);
    memset(out, SPACE, width);
    out[width] = '\0';
    offset = (width - len) / 2;
    for(i=0; i<len; i++){
        int ind = offset + i;
        if(ind >= 0 && ind <width)
            out[ind] = str[i];
    }
    return out;
}

int main(){
    char buf[256];
    int i;
    for(i=1; i<20; i++)
        puts(centering(buf, "abcde", i));
    return 0;
}

空白埋めしてから、部分コピー

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <string.h>

// dest のサイズは w + 1 以上とする事
char *centering(char *dest, const char *s, size_t w) {
    size_t len;

    len = strlen(s);
    sprintf(dest, "%*s", w, "");
    
    if (len >= w) strncpy(dest, s + (len - w) / 2, w);
    else          strncpy(dest + (w - len) / 2, s, len);

    return dest;
}

int main(void) {
    char buff[256];
    printf("|%s|\n", centering(buff, "123456789", 15));
    printf("|%s|\n", centering(buff, "123456789",  5));
}

#4164を見てたら、sprintfを1回呼ぶだけでも出来そうだと思ったので、やってみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <string.h>

char *centering(char *dest, char *s, int w) {
  int d = w - strlen(s);
  
  sprintf(dest, "%*s%.*s%*s", ( d >0 ? d/2 : 0 ), "", ( d >0 ? strlen(s) : w ), s + ( d >0 ? 0 : -d/2 ), ( d >0 ? d/2 : 0 ), "" );
  
  return dest;
}

int main(void) {
  char buff[256];

  printf("|%s|\n", centering(buff, "123456789", 20));
  printf("|%s|\n", centering(buff, "123456789", 5));
}

"%.*s" で幅制限出来たのか... ただ、d > 0 で d が奇数のときに、1桁足りないようです。 それを踏まえて、自分ならこう書きます。

1
2
3
4
5
6
7
8
char *centering(char *dest, char *s, int w) {
  int d = w - strlen(s);
  
  if (d > 0) sprintf(dest, "%*s%*s", w - d/2, s, d/2, "");
  else       sprintf(dest, "%.*s", w, s - d/2);
  
  return dest;
}

あ、確かに。 ご指摘ありがとうございます。 sprintfの所を下記のように修正します。 ですが、youheiさんのような書き方の方が、自分も好みです。
1
2
3
4
  sprintf(dest, "%*s%.*s%*s", 
      ( d>0 ? d - d/2 : 0 ), "", 
      ( d >0 ? strlen(s) : w ), s + ( d >0 ? 0 : -d/2 ), 
      ( d >0 ? d/2 : 0 ), "" );

馬鹿正直に作ってみました。 あらかじめ条件を洗い出して、最後に処理をまとめて行う、というのが個人的なスタイルです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<stdio.h>
#include<string.h>

void centering( char *buffer, int col_num, char* string );

int main( int argc, char *argv[] )
{
    char buf[256];
    
    memset( buf, 0, sizeof(buf) );
    
    centering( buf, 10, "abcdefg" );
    printf( "%s\n", buf );
    
    centering( buf, 8, "0123456789" );
    printf( "%s\n", buf );
    
    centering( buf, 7, "0123456789" );
    printf( "%s\n", buf );
    return 0;
}

void centering( char *buffer, int col_num, char* string )
{
    int copy_len = 0;
    int source_index = 0;
    int target_index = 0;
    
    /* output initialize (using '@' instead of SPACE for check) */
    memset( buffer, '@', col_num );
    buffer[col_num] = '\0';
    
    /* length of string for output */
    copy_len = strlen( string );
    
    /* check which is longer */
    if( copy_len < col_num )
    {
        int diff = col_num - copy_len;
        target_index = diff / 2;
    }
    else
    {
        int diff = copy_len - col_num;
        source_index = diff / 2;
        copy_len = col_num;
    }
    
    memcpy( &buffer[target_index], &string[source_index], copy_len );
    return;
}

Index

Feed

Other

Link

Pathtraq

loading...