Tips in Action‎ > ‎문자열‎ > ‎

숫자에 천 단위로 컴마 넣어주기

전종필

숫자를 출력할 때, 쉼표(, 컴마)를 1000 단위마다 넣어주면 읽기가 편합니다. 아마 CGI 프로그램이라면 이런 경우가 종종 필요하겠죠.
방법은 여러가지가 있을 것입니다. 재귀적인 함수를 만들 수도 있고, 아니면 일단 3자씩 잘랐다가 join으로 묶어 주는 경우도 있을 것입니다. 혹은 정규식을 사용할 수도 있겠습니다.

재귀함수를 만드는 것은 닭잡는 데 소 잡는 칼을 쓰는 격인 듯 합니다.
해서, 먼저 3자씩 잘라서 만드는 경우를 보겠습니다. 길이가 서로 다른 9개의 숫자를 테스트 해 보겠습니다.

$t = time();

$n = 9;
$len = length($t);

while($n--) {
        $num = substr($t, 0, $len - $n);
        $cnum = put_commas($num);
        printf "%12s => %17s\n", "'$num'", "'$cnum'";
}



sub put_commas {
        my $num = shift;
        my $part;
        my @arr;
        while( $part = substr($num, -3, 3, '' ) ) {
                unshift(@arr, $part);
        }
        return join(",", @arr);
}
방법은 뒷쪽에서 3자씩 잘라서 배열에 unshift로 앞쪽으로 밀어넣어서 저장하여 순서를 유지하는 것입니다.

그럼 정규식을 사용하면 어떨까요?

$t = time();

$n = 9;
$len = length($t);

while( $n-- ) {
        $num = substr($t, 0, $len - $n);
        $cnum = $num;
        while( $cnum =~ s/(\d+)(\d{3})\b/$1,$2/ ) { 1; }
        printf "%12s => %17s\n", "'$num'", "'$cnum'";
}
이 경우는 서브루틴이 따로 필요하지 않습니다. while문 하나로 해결이 됩니다.
핵심은 4개 이상의 숫자가 연속으로 있을 경우에는 뒤의 3개 숫자와 앞의 숫자들 사이에 쉼표를 넣어 주는 것을 반복합니다.
그런데 왜 s///g 와 같이 g (global) 옵션을 사용하지 않고 while문을 사용한 것일까요?
s///는 앞에서부터 동작을 시작에서 맨 끝에서 마칩니다. s/(\d+)(\d{3})\b/$1,$2/는 맨 처음부터 숫자의 맨 끝과 일치합니다. 왜냐하면 *나 +같은 분량부호(quantifier)는 기본적으로 ``가능한 많은 갯수와 일치''하도록 되어 있습니다. 따라서 맨 처음 스캐닝에서 맨 끝까지 가기 때문에 한번만 적용되는 것입니다.
위 두 경우 모두 출력되는 내용은

        '10' =>              '10'
       '103' =>             '103'
      '1032' =>           '1,032'
     '10321' =>          '10,321'
    '103214' =>         '103,214'
   '1032142' =>       '1,032,142'
  '10321422' =>      '10,321,422'
 '103214220' =>     '103,214,220'
'1032142206' =>   '1,032,142,206'
입니다.
그러나

while( $cnum =~ s/(\d+)(\d{3})\b/$1,$2/ ) { 1; }
대신에

$cnum =~ s/(\d+)(\d{3})\b/$1,$2/g
를 사용할 경우는 출력이

        '10' =>              '10'
       '103' =>             '103'
      '1032' =>           '1,032'
     '10321' =>          '10,321'
    '103214' =>         '103,214'
   '1032142' =>        '1032,142'
  '10321422' =>       '10321,422'
 '103214220' =>      '103214,220'
'1032142206' =>     '1032142,206'
가 됩니다.

어느 쪽을 사용하여도 좋습니다. 단지 스타일의 문제죠.
글쎄요, 효율을 따진다면, 전 생각도 못해봤지만, 세상살이가 너무 빡빡한게 아닌가요? 
Comments