challenge 文字列のセンタリング

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

Posted feedbacks - Nested

Flatten Hidden

一番乗りかな?きわめてけれんみのない実装。

Dan the Perl Monger

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/usr/local/bin/perl
use strict;
use warnings;

sub center{
    my $str = shift;
    my $width = shift || 80;
    my $margin = int(($width - length($str))/2);
    return $str if $margin == 0;
    return " " x int($margin) . $str if $margin > 0;
    substr($str, 0, -$margin, '');
    return substr($str, 0, $width);
}

chomp and print center($_, 72), "\n" for(<>)
__END__
0         1         2         3         4         5         6         7
012345678901234567890123456789012345678901234567890123456789012345678901
This line is intentionally longer than 72 chars to test center() works fine.

substr()二度もやるのは無駄じゃん。

Dan the Lazier

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
--- center.pl.orig      2007-11-17 04:35:53.000000000 +0900
+++ center.pl   2007-11-17 05:17:39.000000000 +0900
@@ -8,8 +8,7 @@
     my $margin = int(($width - length($str))/2);
     return $str if $margin == 0;
     return " " x int($margin) . $str if $margin > 0;
-    substr($str, 0, -$margin, '');
-    return substr($str, 0, $width);
+    return substr($str, -$margin, $width);
 }

これまたけれんみのない、#4120と同様の実装。rubyらしくStringを拡張して格調高く。

Dan the Occasional Rubyist

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class String
  def centered(width)
    width ||= 80
    margin = (width - self.length)/2;
    return self if margin == 0
    return " " * margin + self if margin > 0
    self[-margin..margin-1]
  end
end

ARGF.readlines.each{|l| puts l.centered(72) }
__END__
0         1         2         3         4         5         6         7
012345678901234567890123456789012345678901234567890123456789012345678901
This line is intentionally longer than 72 chars to test String#centered works fine.
String#centerを使って少し修正しました.
また,入力行がちょうどwidth文字 + \nのときに,頭の一文字と\nを切り落としてしまっていたので,centeredに渡す前にchompするようにしました.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class String
  def centered(width = 80)
    margin = (width - self.length)/2;
    return center(width) if margin >= 0
    self[-margin..margin-1]
  end
end

ARGF.readlines.each{|l| puts l.chomp.centered(72) }
__END__
0         1         2         3         4         5         6         7
012345678901234567890123456789012345678901234567890123456789012345678901
This line is intentionally longer than 72 chars to test String#centered works fine.

さらに修正しました. #4173 で,ご指摘を受けた不具合も直っています.

p "0123456789".centered(3)  # "345"
p "012".centered(5)  # " 012 "
p "012".centered(4)  # "012 "
p "012".centered(3)  # "012"
p "012".centered(2)  # "01"
p "012".centered(1)  # "1"
p "012".centered(0)  # ""
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class String
  def centered(width = 80)
    return center(width) if width > size
    self[(size - width) / 2, width]
  end
end

ARGF.readlines.each{|l| puts l.chomp.centered(72) }
__END__
0         1         2         3         4         5         6         7
012345678901234567890123456789012345678901234567890123456789012345678901
This line is intentionally longer than 72 chars to test String#centered works fine.

String#centerは右側からパディングを入れるようですので,それにあわせて,widthをはみ出す場合は「左側から削る」ようにしました.

Rubyでは,(CやPerlと異なり)負の整数の除算の場合,剰余の符号が除数(divisor)の符号と一致するような商が得られますが,4行目で文字列を削る際にその性質を利用しています. (4171 を見てぱくりました.勉強になります:-)

p "0123456789".centered(3)  # "456"
p "012".centered(5)   # " 012 "
p "012".centered(4)   # "012 "
p "012".centered(3)   # "012"
p "012".centered(2)   # "12"
p "012".centered(1)   # "1"
p "012".centered(0)   # ""
p "012".centered(-1)  # "012"
p "012".center(4) == " 012 ".centered(4)  # true
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
--- center.rb.orig      2007-11-19 17:17:47.000000000 +0900
+++ center.rb   2007-11-19 17:17:39.000000000 +0900
@@ -1,7 +1,7 @@
 class String
   def centered(width = 80)
-    return center(width) if width > size
-    self[(size - width) / 2, width]
+    return center(width) if width > size or 0 > width
+    self[-((width - size) / 2), width]
   end
 end

(width.odd? xor self.odd?) のときに、余計に1文字削ってしまうと思います。

1
p "0123456789".centered(3)  => "45"

Pythonは組み込みでstr#center()を持ってたりするので一番楽かも。

Dan the Occasional Pythonista

P.S. perl の while(<>)、RubyのARGV.readlinesの代わりってpythonでどう書くのかにゃ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/usr/bin/python

def center_and_crop(str, width = 80):
    margin = (width - len(str))/2
    if margin >= 0: return str.center(width)
    else:           return str[-margin:width-margin]

if __name__ == '__main__':
  import sys
  for line in sys.stdin:
    print center_and_crop(line[:-1], 72)

#         1         2         3         4         5         6         7
#12345678901234567890123456789012345678901234567890123456789012345678901
#This line is intentionally longer than 72 chars to test center_and_crop works \
fine.

初投稿です。 アカウントを作ってログインしようとしたら This account is inactive. と言われてしまいました。 何でだろう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
(use srfi-13)

(define (string-pad-both string width)
  (let* ((slen (string-length string))
         (left (string-take string (quotient slen 2)))
         (right (string-drop string (quotient slen 2)))
         (llen (quotient width 2))
         (rlen (- width llen)))
    (string-append
     (string-pad left llen)
     (string-pad-right right rlen))))

(define (center string . args)
  (string-pad-both string (get-optional args 80)))

ごめんなさい。ログインできました。 メールが届いてるのに気づきませんでした。 英語のメッセージを真面目に読んでいなかったせい?

投稿者を修正しておきました。

気づくのが遅れてしまいましたが、修正ありがとうございました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import std.stdio;
import std.string;
import std.utf;

string cropCenter(string str, uint width){
    auto wstr = toUTF16(str);
    int leftMargin = cast(int)(width - wstr.length) / 2;
    int rightMargin = cast(int)(width - wstr.length) - leftMargin;
    return (rightMargin < 0) ?
           toUTF8(wstr[-leftMargin..($ + rightMargin)]) :
           (repeat(" ", leftMargin) ~ str ~ repeat(" ", rightMargin));
}

void main(){
    writefln(cropCenter("ほげら", 2));  //=> "ほげ"
    writefln(cropCenter("ほげら", 1));  //=> "げ"
    writefln(cropCenter("ほげ",   2));  //=> "ほげ"
    writefln(cropCenter("ほげ",   1));  //=> "ほ"
    writefln(cropCenter("ほげら", 5));  //=> " ほげら "
    writefln(cropCenter("ほげら", 4));  //=> "ほげら "
    writefln(cropCenter("ほげ",   5));  //=> " ほげ  "
    writefln(cropCenter("ほげ",   4));  //=> " ほげ "
}
C# だと PadReft っていう指定文字数になるまで空白文字を埋め込むメソッドがあります。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
static class Program {
    static void Main() {
        string[] texts = {
            "0123456789",
            "abc",
            "abcdefg",
            "abcdefghijklmn",
        };
        foreach(string text in texts) {
            Console.WriteLine(Centering(text, 10));
        }
    }
    static string Centering(string text, int width) {
        int margin = (int)((width - text.Length) / 2);
        if (margin == 0)
            return text;
        if (0 < margin)
            return text.PadLeft(margin + text.Length);
        return text.Substring(-margin, width);
    }
}

	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def centering(lst,n)
  return lst if (n <= 0 || lst == "")
  c = (lst = " "*n + lst + " "*n).size/2
  lst[c-n/2..-1][0..n-1]
end

if __FILE__ == $0
  p (s = centering("end",4))  != " end" ? s : :ok
  p (s = centering("end",3))  != "end"  ? s : :ok
  p (s = centering("end",2))  != "en"   ? s : :ok
  p (s = centering("end",1))  != "n"    ? s : :ok
  p (s = centering("end",0))  != "end"  ? s : :ok
  p (s = centering("end",-1)) != "end"  ? s : :ok
  p (s = centering("",0))     != ""     ? s : :ok
  p (s = centering("",1))     != ""     ? s : :ok
end
CL の format で。もっときれいに書けそうですが。
1
2
3
4
5
(defun center (str &optional (width 80))
  (let ((margin (- (length str) width)))
    (if (> margin 0)
        (format t "~a" (subseq str (floor (/ margin 2)) (+ width (floor (/ margin 2)))))
      (format t (format nil "~~~a:@<~~a~~>" width) str))))
最後の行みたいなときは V を使うと短くなりますよ
(format t "~V:@<~A~>" width str)
kozima さん、ご指摘感謝です。
format での~v ってよく調べたら基本的機能なのですね。参考になりました。
1
2
3
4
5
6
(defun center (str &optional (width 80))
  (let ((margin (- (length str) width)))
    (format t "~v:@<~a~>" width 
        (if (> margin 0)
        (subseq str (floor (/ margin 2)) (+ width (floor (/ margin 2))))
          str))))
Squeak Smalltalk で。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
| centeredOf |
centeredOf := [:line :width |
    | size offset centered start end delta |
    size := line size.
    delta := (width - size + 1) // 2.
    offset := (delta min: 0) negated + 1.
    start := (delta max: 0) + 1.
    end := delta + size min: width.
    centered := String new: width withAll: Character space.
    centered replaceFrom: start to: end with: line startingAt: offset].

centeredOf value: '123456789' value: 15.  "=> '   123456789   ' "
centeredOf value: '123456789' value: 5.   "=> '34567' "
特にひねりはありません。さらに簡単にできそうな気がします。
 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;
}
バグの報告です。
ヌル文字が足りず、変なオマケが後に付きます。
mallocの実装によって気づかないことがあるかもしれません。
私の場合は#が出力されました。丁寧に範囲外の1byteめに'#'をセットしてくれているようです。
PC-E500のベーシックで書いたなこんなの。
今は、schemeで。
実行結果
------
$ gosh 87.scm
1234567890
    abc
  abcdef
  abcdefg
bcdefghijk
-------
奇数文字の時は、右に寄ります。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(define (centering s w)
  (let* ((p (make-string (quotient w 2) #\ ))
         (ss (string-append p s p))
         (sw (string-length ss))
         (mg (quotient (- sw w) 2)))
    (substring ss mg (- sw mg))))

(print "1234567890")
(print (centering "abc" 10))
(print (centering "abcdef" 10))
(print (centering "abcdefg" 10))
(print (centering "abcdefghijkl" 10))

if文もcenterメソッドも使わずに。

1
2
3
4
5
def center(s, n):
  return (' ' * n + a + ' ' * n)[(len(a)+n)/2:][:n]

print center('abc', 9)
print center('abc', 1)

短くて素敵ー。 sがaになってますよ。

あ、ほんとだ。ご指摘感謝です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function center(s, w) {
    var r=[], d=w-s.length;
    if (d < 0) { s=s.substring(d/2*-1, s.length+d/2); }
    for (var i=0; i<=d/2-1; i++) { r.push(" "); }
    r.push(s);
    for (var i=0; i<=d/2-1+d%2; i++) { r.push(" "); }
    return r.join("");
}
console.log("[" + center("test", 0) + "]");    // => []
console.log("[" + center("test", 5) + "]");    // => [test ]
console.log("[" + center("test", 6) + "]");    // => [ test ]
console.log("[" + center("test", 10) + "]");    // => [   test   ]
console.log("[" + center("testtest", 10) + "]");    // => [ testtest ]
> for(s in c("1234567890", "abc", "abcdef", "abcdefg", "abcdefghijkl")){
+     print(center(s, 10))
+ }
[1] "1234567890"
[1] "   abc    "
[1] "  abcdef  "
[1] " abcdefg  "
[1] "bcdefghijk"
1
2
3
center <- function(s, n){
    format(substring(s, (i<-(nchar(s)-n)/2+1), i+n-1), width=n, justify="centre")
}

Javaがなかったので初投稿です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class Answer87 {
    public static String formatCenter(String str, int length) {
        if (length <= 0) return str;
        StringBuilder builder = new StringBuilder(str);
        for (int index = 0; index < length; index++) {
            builder.insert(0, ' ');
            builder.append(' ');
        }
        int start = (builder.length() - length) / 2;
        return builder.substring(start, start + length);
    }

    public static void main(String[] args) {
        System.out.println(formatCenter("abcde", 5));    // "abcde"
        System.out.println(formatCenter("abcde", 7));    // " abcde "
        System.out.println(formatCenter("abcde", 8));    // "  abcde "
        System.out.println(formatCenter("abcde", 1));    // "c"
        System.out.println(formatCenter("abcde", 2));    // "bc"
        System.out.println(formatCenter("abcde", 0));    // "abcde"
    }
}

sayはprint lnだと思ってください。 仕様と有ってるのか・・・どうか

1
2
3
4
5
6
7
8
var src = '123456789'
say( '[' + centering(src,100)  + ']' );
say( '[' + centering(src,5)    + ']' );
function centering( s , w ){
  if( s.length < w ) return space( ( w / 2 - s.length / 2 ) ) + s + space( ( w / 2 - s.length / 2 ) );
  else               return s.substring( ( s.length - w ) / 2, s.length - ( ( s.length - w ) / 2 ) );
  function space (l){ var r = ''; while( r.length < l ) r += ' '; return r; }
}
C++ では boost::format でセンタリングができるようです。
 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
#include <cstdlib>
#include <iostream>
#include <sstream>
#include "boost/format.hpp"

using namespace std;
using namespace boost;

string &center( string &str, int width ){
   int len = str.length();
   if( len > width ){
      str = str.substr( (len-width+1)/2, width );
   }
   ostringstream oss;
   oss << "%1$=" << width << "s";
   str = ( format(oss.str()) % str ).str();
   return str;
}

int main ( int argc, char *argv[] ){
   int width( atoi(argv[2]) );
   string str( argv[1] );
   cout << center( str, width ) << endl;
   return EXIT_SUCCESS;
}
1
2
3
4
5
6
7
8
9
center :: Int -> [Char] -> [Char]
center n str | slen < n  = center'
             | slen > n  = clop
             | otherwise = str
  where
    slen = length str
    center' = lmargin ++ str ++ rmargin
    (lmargin, rmargin) = splitAt ((n-slen) `div` 2) $ replicate (n-slen) ' '
    clop = take n $ drop ((slen-n) `div` 2) str
ビット演算(必ず整数が返る)で曖昧さを除く。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
String.prototype.center = function(width){ var d, s;
  return (d = (width |= 0) - this.length) > 0
    ? (s = Array((d >> 1) + 1).join(' ')) + this + (d & 1 ? s + ' ' : s)
    : this.substr(-d >> 1, width);
};

(typeof alert != 'undefined' ? alert :
 typeof print != 'undefined' ? print :
 function($){ typeof WSH == 'object' && WSH.echo($) })((