숫자에 세자리마다 콤마찍는 함수 최적화

saxboy의 이미지

요 며칠 외주로 하는 일 하나에서 마무리 단계에 숫자에 세자리마다 콤마를 찍어달라고 하더군요. 별 생각 없이 만들다가 문득 재미있겠다는 생각이 들어 올려봅니다.

방법 1.
3으로 몫과 나머지를 구하고 나머지부분을 먼저 strcpy, 이후 몫만큼 루프를 돌면서 strcpy & 콤마 찍기

그런데, 코드를 만들려고 생각하니 참 일관성이 없더군요. 다시 다른 방법을 생각해보았습니다.

방법 2. 스트링 마지막부터 하나씩 카피하면서 3자리마다 콤마넣어주기.

이렇게 생각해보니 첫번째 아이디어보다는 웬지 깔끔해지더군요. 이렇게 만든 함수가 요놈입니다.

int util_add_comma_to_num(const char *str, char *buf, int buflen)
{
        int len=0;
        int num_commas=0;
        int comma_count=0;
        int i=0;
        int n=0;

        /* count given string */
        len = strlen(str);
        num_commas = (int)(len/3);

        assert( buflen >= (len + num_commas +1));

        buf[len + num_commas] = '\0';
        for (i=len-1, n=1; i>=0; i--, n++)
        {
                buf[i + num_commas - comma_count] = str[i];

                if ( (n%3)==0 )
                {
                        comma_count++;
                        buf[i + num_commas - comma_count] = ',';
                }
        }
}

#ifdef __TEST__
int main(int argc, char **argv)
{
        char buf[1024]={0,};

        util_add_comma_to_num("12345678000", buf, 1024);
        printf("%s\n", buf);
}
#endif

굉장히 간단한 코드인데, 역시나 스트링처리이다보니 귀찮은 일이 생기고, 구현 여하에 따라서 웬지 위에 적은 것은 비교도 되지 않을 정도로 깔끔하게 만들 수 있겠다는 느낌이 얼핏들더군요. 다른 분들은 어떻게 만드실지 무척 궁금해져서 스레드를 하나 만들어봅니다. 다른 아이디어 또는 깔끔한 구현! 당신의 능력을 보여주세요.

ps. 웬지 recursive로 만들어도 재미있겠다는 생각이 듭니다.

댓글

맹고이의 이미지

http://bbs.python.or.kr/viewtopic.php?t=20504

예전에 본 기억이 있어서 링크를 남겨봅니다. ^^;

cdpark의 이미지

위 프로그램은 문자열 길이가 3의 배수일 경우 잘못 동작합니다. :(

그리고 굳이 뒤에서부터 복사할 필요 있나요?

int
util_add_comma_to_num(const char *str, char *buf, int buflen)
{
    int len;
    int shift;

    /* count given string */
    len = strlen(str);
    shift = -len;

    assert( buflen >= (len + len/3 +1));

    while (*str)
    {
        *buf++ = *str++;
        if (++shift && (shift % 3) == 0)
            *buf++= ',';
    }

    *buf = '\0';

    return 0;
}

사족: "토론, 토의"보다는 그냥 "프로그래밍 Q&A" 쪽에 올리셨어도...

토끼아빠의 이미지

와~
굉장한 고수이시네요~

^^__^^ 나도 언제묜???

좋은 하루 되세요!!

saxboy의 이미지

Quote:
위 프로그램은 문자열 길이가 3의 배수일 경우 잘못 동작합니다.

아유... 창피... :oops:

조금전에 생각하면서는 웬지 뒤부터 복사를 하면 참 깔끔해진다고 생각했는데, 다시 보니 그렇지도 않군요. shift가 참 멋진 것 같아요.

Quote:
사족: "토론, 토의"보다는 그냥 "프로그래밍 Q&A" 쪽에 올리셨어도...

웬지 퍼즐같은 기분이라 토론쪽에 올려두었는데, 별로 적절하지 않을까요? 그렇다면 관리자님께서 옮겨주시면 좋겠습니다.
shr의 이미지

예전에 어디선가 본 Duff's device 라는 것이 생각나서... 8)

int util_add_comma_to_num2(const char *str, char *buf, int buflen)
{
	int len, src, dst;

	len=strlen(str);
	src = 0;
	dst = 0;

	assert(len > 0);
	if (len <= 0) return -1;

	assert(buflen >= len + ((len - 1) / 3) + 1);
	if (buflen < len + ((len - 1) / 3) + 1) return -1;

	switch ((len + 2) % 3) {
		while (src < len) {
			buf[dst++] = ',';
			case 2:
			buf[dst++] = str[src++];
			case 1:
			buf[dst++] = str[src++];
			case 0:
			buf[dst++] = str[src++];
		}
	}
	buf[dst++] = '\0';
   return 0;
}
cdpark의 이미지

while loop 내부로의 (goto) switch라.. 상당히 맘에 안 드는 코드로군요. :)

eplus2의 이미지

cdpark wrote:
int
util_add_comma_to_num(const char *str, char *buf, int buflen)
{
    int len;
    int shift;

    /* count given string */
    len = strlen(str);
    shift = -len;

    assert( buflen >= (len + len/3 +1));

    while (*str)
    {
        *buf++ = *str++;
        if (++shift && (shift % 3) == 0)
            *buf++= ',';
    }

    *buf = '\0';

    return 0;
}

피연산자가 음수일때 % 연산 결과는 기계마다 다르다고 봤는데...
제가 옛날 책을 보고 있는 건가요??

bjs715의 이미지

int util_add_comma_to_num(const char *str, char *buf, int buflen)
{
    int i = 0;
    int pos = 0;

    assert(buflen >= ((strlen(str) * 4) / 3 + 1));

    memset(buf, 0, buflen);
    pos = strlen(str) % 3;
    strncpy(buf, str, pos);

    for (i = pos; i < strlen(str); i+=3)
    {
        if (i > 0) strcat(buf, ",");
        strncat(buf, str + i, 3);
    }

    return 0;
}

이건 어떨까요?

cdpark의 이미지

eplus2 wrote:
피연산자가 음수일때 % 연산 결과는 기계마다 다르다고 봤는데...
제가 옛날 책을 보고 있는 건가요??

-2 % 3 에 대해 1을 돌려줄 수도 있고, -2를 돌려줄 수도 있으니깐요. 하지만 -3 % 3 에 대해서는 0을 돌려주겠죠? (면피!)

방준영의 이미지

cdpark님 버전을 약간더 줄여봤습니다.

/*
 * Put a comma after every 3 digit.
 *
 * buf must be large enough to hold a result string.
 */
void
clarify_number(const char *str, char *buf)
{
	int len;

	len = strlen(str);
	assert(len > 0);

	while (*str) {
		*buf++ = *str++;
		if (--len && (len % 3) == 0)
			*buf++= ',';
	}

	*buf = '\0';
}
arith의 이미지

int util_add_comma_to_num (const char *str, char *buf, int buflen) {
    int len;

    len = strlen(str);
    assert(buflen >= (len + len/3 + 1));

    switch (len % 3) {
        case 2: *buf++ = *str++;
        case 1: *buf++ = *str++;
        case 0: break;
    }

    for (;*str;) {
        *buf++ = ',';
        *buf++ = *str++;
        *buf++ = *str++;
        *buf++ = *str++;
    }

    *buf = '\0';

    return 0;
}
방준영의 이미지

arith님의 코드는

1
12
,123
1,234
12,345
,123,456
1,234,567
12,345,678
,123,456,789

와 같은 결과를 내는군요.

pynoos의 이미지

방준영 wrote:
cdpark님 버전을 약간더 줄여봤습니다.

/*
 * Put a comma after every 3 digit.
 *
 * buf must be large enough to hold a result string.
 */
void
clarify_number(const char *str, char *buf)
{
	int len;

	len = strlen(str);
	assert(len > 0);

	while (*str) {
		*buf++ = *str++;
		if (--len && (len % 3) == 0)
			*buf++= ',';
	}

	*buf = '\0';
}

좀 더 줄여 볼까요?

void 
clarify_number(const char *str, char *buf) 
{ 
        int len; 

        len = strlen(str); 
        assert(len > 0); 

        while ( (*buf++ = *str++) ) {
                if (--len && (len % 3) == 0) 
                        *buf++= ','; 
        }       

        *buf = '\0'; 
}
shr의 이미지

위의 Duff's device 스타일의 코드를 다시 좀 더 짧고 보기 좋게 (?) 써 봤습니다. switch () 문의 브레이스도 생략이 가능하다는군요!

void util_add_comma_to_num2(const char *str, char *buf, int buflen)
{
	int len = strlen(str);

	assert(len > 0);
	assert(buflen >= len + ((len - 1) / 3) + 1);

	switch ((len - 1) % 3)
		while (*str) {
					  *buf++ = ',';
			case 2: *buf++ = *str++;
			case 1: *buf++ = *str++;
			case 0: *buf++ = *str++;
		}
	*buf++ = '\0';
} 

singlet의 이미지

방준영님이나 pynoos 님 버전 정도라면 strcpy() 와 꽤나 비슷해져서, 아예 strcpy() 에 맞춰 argument 순서와 반환 타입을 바꿔 봤습니다. 그 외에 사소한 차이점이 좀 더 있습니다. :)

char *
clarify_number(char *buf, const char *str)
{
    char   *saved = buf;
    size_t  len   = strlen(str);
    while ((*buf++ = *str++) != '\0')
        if (--len && (len % 3) == 0)
            *buf++ = ',';
    return saved;
}

p.s. 개인적으로는 중간에 쓸데없는 비교를 더 이상 하지 않는 Duff's device 쪽이 마음에 듭니다. 물론 코드가 더 괴상하긴 하지만요. :)

cdpark의 이미지

int util_add_comma_to_num (const char *str, char *buf, int buflen) { 
    int len; 

    if (!*str) {
        *str = '\0';
        return 0;
    }

    len = strlen(str); 

    assert(buflen >= (len + len/3 + 1)); 


    switch (len % 3) {
        case 0: *buf++ = *str++;
        case 2: *buf++ = *str++; 
        case 1: *buf++ = *str++; 
    }

    while (*str) {
        *buf++ = ',';
        *buf++ = *str++; 
        *buf++ = *str++; 
        *buf++ = *str++; 
    } 

    *buf = '\0'; 

    return 0; 
}

arith 님의 코드가 맘에 드네요. 버그가 있으면 잡으면 되죠.

dreampia의 이미지

조금더 고쳐서 0으로 시작, 앞뒤 공백, +/- 부호 까지 처리하게 ^^;

char *
clarify_number(char *str, char *buf)
{
    int len;
    char *p;

    if (*str == '\0') {
        *buf = '\0';
        return((char *)NULL);
    }

    /* strip space backward */
    p = str + strlen(str) - 1;
    for ( ; *p == ' ' ; p--) ;
    *(++p) = '\0';
    /* strip space forward */
    for ( ; *str == ' ' || *str == '0'; str++) ;

    p = buf;

    if (*str == '+') *str++;
    if (*str == '-') *buf++ = *str++;

    if ((len = strlen(str)) <= 0) {
        *buf = '\0';
        return((char *)NULL);
    }

    while ( (*buf++ = *str++) ) {
        if (--len && (len % 3) == 0)
            *buf++= ',';
    }

    *buf = '\0';
    return(p);
}

>/dev/null 2>&1

doldori의 이미지

C++로 해봤습니다. :D

#include <iostream>
#include <string>

using std::string;

void insert_separator(string& s, char separator = ',', int width = 3)
{
	string::iterator i = s.end() - width;
	while (i > s.begin())
		i = s.insert(i, separator) - width;
}

int main()
{
	string s("123456789");
	insert_separator(s);
	std::cout << s << std::endl;
}
pynoos의 이미지

소수점이하도 처리하실 분? ;)

saxboy의 이미지

int util_add_comma_to_num(const char *str, char *buf, int buflen)
{       
        int w=0, i=0;
        int len=0;

        len = strlen(str);
        w = len %3;
        w = w ? w : 3;
        for (i=0; i<w; i++) *buf++ = *str++; 

        if ( len <3 ) 
                return 1; 
        else
        {
                if ( len>3) *buf++=',';
                return util_add_comma_to_num( str, buf, buflen-w ); 
        }
        return 1; 
}

꼬리에 덧붙여놓았던 recursive 버전 하나 만들어봅니다.

방준영의 이미지

cdpark님의 것을 제가 수정한 것을 pynoos님이 수정한 것을 다시 수정한 버전. 마지막에 붙는 buf = '\0';를 삭제했습니다.

void 
clarify_number(const char *str, char *buf) 
{ 
        int len; 

        len = strlen(str); 
        assert(len > 0); 

        while (*buf++ = *str++) { 
                if (--len && (len % 3) == 0) 
                        *buf++= ','; 
        }        
}

Duf's device(?) 코드를 경고 안나게 수정한 버전.

void util_add_comma_to_num2(const char *str, char *buf, int buflen) 
{ 
   int len = strlen(str); 

   assert(len > 0); 
   assert(buflen >= len + ((len - 1) / 3) + 1); 

   switch ((len - 1) % 3) {
   case 3: /* fake label to make gcc happy */
      while (*str) { 
                 *buf++ = ','; 
         case 2: *buf++ = *str++; 
         case 1: *buf++ = *str++; 
         case 0: *buf++ = *str++; 
      } 
   }
   *buf = '\0'; 
}
bjs715의 이미지

cdpark님 버전을 조금 더 줄여보았습니다.

void clarify_number(const char *str, char *buf)
{
    while (*buf++ = *str++) {
        if ((strlen(str) % 3) == 0) *buf++= (*str ? ',' : '\0');
    }
}

좀 더 정확히 얘기하자면
cdpark님 -> 방준영님 -> pynoos님 으로 수정된 버전이지요...

alwaysN00b의 이미지

성능은 어떻게 될까요? 무조건 라인수 적다고 빠른건 아니겠죠?

void util_add_comma_to_num2(const char *str, char *buf, int buflen) 
{ 
	int len;
	len = strlen(str);
	while(*buf++ = *str++)
   	if(  --len && !(len % 3) ) *buf++ = ',';   		
}

제가 초보라, 언뜻 보기엔 이게 젤 빠를것 같은데 아닌가요?
(안전성은 고려하지 않구요)

언제나 시작

cdpark의 이미지

arith 님의 코드를 제가 고친 버젼, 혹은 Duff's device를 쓴 버젼(결국 둘은 같습니다.) 등이 가장 빠를겁니다.

컴파일러의 최적화 루틴은 소스가 긴 편을 선호하므로 제가 고친 버젼을 좋아할거라고 주장합니다. :)

pentium 4의 깊은 파이프라인을 위해서는 loop를 더 풀어야할지도 모르고요. :)

방준영의 이미지

Duff's device가 뭔가 해서 찾아봤더니 이런 것이었군요.
http://info.astrian.net/jargon/terms/d/Duff_s_device.html

정말 멋진 아이디어입니다. 그런데 저만 몰랐나요... :wink:

qprk의 이미지

예전에 만들어 둔것이 있었내요.. :)

//세자리마다 컴마찍기////////////////////////////////////////////////////////////////////////////
	public String NumberProc(int num){
		String srcString = Integer.toString(num);
		int len = srcString.length();
		resultNumber = "";
		if(len > 3){
			if(len%3 != 0){
				resultNumber = resultNumber + srcString.substring(0,len%3)+",";
			}
			srcString = srcString.substring(len%3);
			len = srcString.length();

			while(len > 3){
				resultNumber = resultNumber + srcString.substring(0,3)+",";
				srcString = srcString.substring(3);
				len = srcString.length();
			}
			resultNumber = resultNumber + srcString;
		} else{ // 세자리수보다 작다
			return srcString;
		}

		return this.resultNumber;
	}

멋진남자...

cedar의 이미지

qprk wrote:
예전에 만들어 둔것이 있었내요.. :)

자바는 JDK에서 지원할 텐데 굳이 만드실 필요는... :?
qprk의 이미지

cedar wrote:
qprk wrote:
예전에 만들어 둔것이 있었내요.. :)

자바는 JDK에서 지원할 텐데 굳이 만드실 필요는... :?

할줄아는건 c 뿐이고..
자바로 만들라하니...

위와같은 코드가 나오내요...

자바 너무 어려워요...

멋진남자...

cedar의 이미지

doldori wrote:
C++로 해봤습니다. :D

ANSI C++도 Java 처럼 천단위 컴마 찍기를 지원합니다.
표준 라이브러리의 locale 클래스를 사용하면 되지요.
std::string sep_thousands(double f)
{
    using namespace std;

    const char *locale_name =
    #ifdef WINDOWS
    	"korean";
    #endif
    
    ostringstream oss;
    oss.imbue(locale(locale_name));
    oss << f;
    return oss.str();
}

로케일을 한국어나 영어로 설정하면 자동으로 천단위 컴마 찍기가 됩니다.
(소숫점 이하는 찍히지 않습니다.)
만약 독일어로 설정하면 컴마(,) 대신 피리어드(.)가 찍히죠.

ANSI C에서도 로케일을 써서 이렇게 할 수 있는 지는 모르겠습니다.
제가 C는 잘 몰라서요. :oops:

Fe.head의 이미지

cedar wrote:
doldori wrote:
C++로 해봤습니다. :D

ANSI C++도 Java 처럼 천단위 컴마 찍기를 지원합니다.
표준 라이브러리의 locale 클래스를 사용하면 되지요.
std::string sep_thousands(double f)
{
    using namespace std;

    const char *locale_name =
    #ifdef WINDOWS
    	"korean";
    #else
    	"ko_KR";
    #endif
    
    ostringstream oss;
    oss.imbue(locale(locale_name));
    oss << f;
    return oss.str();
}

로케일을 한국어나 영어로 설정하면 자동으로 천단위 컴마 찍기가 됩니다.
(소숫점 이하는 찍히지 않습니다.)
만약 독일어로 설정하면 컴마(,) 대신 피리어드(.)가 찍히죠.

ANSI C에서도 로케일을 써서 이렇게 할 수 있는 지는 모르겠습니다.
제가 C는 잘 몰라서요. :oops:

음.. 제가 한번 해봤는데. 안되는데.. 왜 안되는거죠?

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

std::string sep_thousands(double f)
{
    using namespace std;

    const char *locale_name =
#ifdef WINDOWS
    "korean";
#else
    "ko_KR";
#endif

    ostringstream oss;
    oss.imbue(locale(locale_name));
    oss << f;
    return oss.str();
}

int
main()
{
    cout << sep_thousands(100000) << endl;
    return 0;
}

결과
100000

ko_KR를 ko-KR, korea 모두 바꾸어 봐도 안돼는군요..

LINUX 환경입니다.

고작 블로킹 하나, 고작 25점 중에 1점, 고작 부활동
"만약 그 순간이 온다면 그때가 네가 배구에 빠지는 순간이야"

keedi의 이미지

In Perl...

1. CPAN에서 Tie::Comma 모듈 설치
2. Tie::Comma 패키지가 Export하는 %comma 변수를 사용해서 숫자 변환
3. 음수 소수점 상관없이 동작

#!/usr/bin/perl 
 
use Tie::Comma;
 
my $a = 1234567.89;
 
print "With commas: $comma{$a}\n";

---------------------------
Smashing Watermelons~!!
Whatever Nevermind~!!

Kim Do-Hyoung Keedi

----
use perl;

Keedi Kim

goeasylife의 이미지

sub commify {
    my $text = reverse $_[0]; #숫자를 역으로
    $text =~ s/
         (\d{3})  #3개의 연속된 숫자
        (?=\d)   #그다음에 숫자가나오는(lookaheads)
         (?!\d*\.)#소숫점 이하부문은 무시(lokkbehinds)
       /$1,/gx;
    return scalar reverse $text;
}

이렇게 구현도 가능해요^^

임창진의 이미지

&lt;D1&gt;&lt;CDR&gt;=$1$2;&lt;D2&gt;&lt;CDR&gt;=$1$2;&lt;D3&gt;&lt;CDR&gt;=$1$2;CDR:&lt;D3&gt;=,$1;&lt;D&gt;=@fail;=@end

--------

설명

<D1> 은 1자리 숫자
<D2> 는 2자리 숫자
<D3> 은 세자리 숫자

<D> 는 자릿수 관계없이 연속된 숫자

모든숫자는 , 찍는 관점에서 보면 3종류로 나누어진다
첫번째는

1,234
1,234,456

처럼 처음의 , 나오기전에 숫자가 1개인것

두번째는

12,345
12,344,567

처럼 처음의 , 나오기전에 숫자가 2개인것

그리고 마지막으로

123,456
123,456,789

처럼 처음의 , 나오기전에 숫자가 3개인것

그리고 처음 콤마를 포함한 뒷자리는 , 와 3자리 숫자의 반복이다.

<CDR> 은 이 3자리 숫자의 반복을 패턴으로잡아내어 그 사이에 , 를 붙인다.

<CDR> 은 어떻게 3자리 숫자를 찾는가?

CDR 은 3개의 룰로 구성되어있는데

첫번째는
<D3>=,$1
3자리 숫자를 만나면 숫자앞에 , 를 붙인다.

두번째는
<D>=@fail
숫자를 만나면 지금 처리하는 문자는 CDR 패턴에 맞지 않는다라고 알려준다.

세번째는
=@end
성공적으로 처리를 끝낸다. (지금 처리한 문자열은 CDR 패턴이다.)

12345678 을 변화하는 예를 들면
맨처음 <D1><CDR>에 12345678 이 맞는지 비교한다.
<D1>=1
<CDR>=2345678 이어야 패턴에 맞는데
CDR 에 2345678 을 매칭시켜보면

(룰 적용 순서는 먼저정의된 룰먼저 적용하고 룰이 적용되면 이후 스트링은 룰의 처음부터 다시적용된다)

CDR 의 처음룰에 의해 234 이 ,234 으로 변화되고
나머지 45678 에 대해서
CDR 룰의 처음부터 다시적용하면 567 이 ,567 으로 변환되고
룰이 적용되었으니 나머지 8 에 대해 CDR 룰의 처음부터 다시적용하면
첫번째 룰은 3자리수 이어야 하는데 남은것은 8 하나이니 첫번째 룰은 실패
두번째 룰을 적용하면 숫자일경우 @fail 인데 8은 숫자이므로 여지껏 변환한거 롤백함 실패응 돌려줌
다시 처음으로 돌아와서
결국 <D1><CDR> 패턴이 CDR 에서 실패함으로써 결국 맞지않았으므로
12345678 을 두번째 룰 <D2><CDR>을 적용해 본다.

그러면 아까와 같은 방법으로 <D2> 에 해당하는 12 를 제외한 345678 을 <CDR> 에 적용해보면
CDR 의 첫번째 패턴이 두번적용되면 세번째 적용할때 CDR 의 세번째 룰 =@end 에 의해 성공적으로 매칭이 끝난다.

그러면 <D2><CDR>=$1$2 에 의해 D2 -> 12 CDR -> ,345,678 로 치환된다.
-------------------------------------------------------------------

그리고... Gema 쓰시는분 어디 없나요?

익명사용자의 이미지

money' (o:[])     = o:[]
money' (o:t:[])   = o:t:[]
money' (o:t:h:[]) = o:t:h:[]
money' (o:t:h:ms) = o:t:h:',':money' ms
money = reverse.money'.reverse

간단하죠?
rx78gd의 이미지

import java.text.DecimalFormat; // 숫자를 원하는 형식으로 바꿔 문자열로 바꿔주는 API
import java.io.*;

public class Money
{
public static void main(String args[]) throws IOException
{

...... 변수명들 생략 ......

String checkDay = null;
String checkMoney = null;

....입력받은 것이 숫자인지 체크하는 부분 생략......

checkDay = new DecimalFormat("#,###").format(y);
checkMoney = new DecimalFormat("#,###").format(saveMoney);

......중간 왕창생략..^^......
}

이런식으로 사용중입니다. 변수명 'y'에는 날짜를 받아와서 '1,234' 일... 이런식으로 찍어주고 'saveMoney'에는 누적된 합계금액을 써서 '1,234,856' 원... 이럭식으로 콤마를 찍어주고 있죠. 원래 예전 쓰레드에 글 올린적이 있는 하루에 10원씩 늘여가면서(첫날 10원, 둘째날 20원 이런식...) 저금하면 키보드로 입력받은 날짜의 누적금액은 얼마인가하는 호기심에 15분간에 걸쳐 뚝딱거려 만든 소스중 일부였죠.^^

예전 1.3때는 만들어 썼는데 1.4때부터 DecimalFormat 로 사용하니 편하더라구요.^^

-------------------------------------------------------------------------------------------
나에겐 할 수 있다는 의지와
하면 된다는 신념과
해야 한다는 의무가 있다.

http://rx78gd.egloos.com

-------------------------------------------------------------------------------------------
나에겐 할 수 있다는 의지와
하면 된다는 신념과
해야 한다는 의무가 있다.

http://rx78gd.tistory.com

오호라의 이미지

몇달전에 봤던 한글과컴퓨터 면접 실기테스트 3번문제였던가. 이 문제였습니다.

> -100000
>> -100,000

> +1000000
>> +1,000,000

> 100000
>> 100,000

신기하네요. 랭귀지가 같은 이상 길은 하나인가. 매번 느끼는거지만. 모든 길은 로마로 통한다!!

제 개인적인 생각으로 루핑을 푸는것도 중요하지만, 루프안의 조건문을 최대한 줄이는거죠.

루프안의 조건문은 최적화를 방해하는 요소인듯 합니다.

이 문제 특성상 플밍스킬이 조그만 있어도 쉽게 풀수 있지만. 루프->조건

조금 생각해보면 루프안의 조건을 쉽게 풀수 있습니다.

Hello World.

오호라의 이미지

작년에 한글과컴퓨터 면접용 1번문제였네요. 그 당시 컴이 갑자기 맛이가서리 하루밖에 시간이 없어서 6문제중 5문제만 풀어서 제출했는데 나중에 문서를 잘 일어보니까. 한문제당 20점 총 120점, 100점이하는 무조건 탈락. ㅡㅡ;

결국 5문제를 만점 받아도 한문제라도 못풀면 자동탈락이었다는..ㅋㅋㅋ

주석이 빠진걸 보니까. 최종 제출본은 아니었네요. 찾으면 다시 수정하겠씁니다.

#define CIPER (float)(3.0)
#define MAGIC_NUMBER (float)(0.7)

두 값을 수정하면 자릿수 변경이 가능합니다,

지금와서 보니 위의 더프의 디바이스와 거의 비슷하죠!? ^^; 이런 사실을 소스 냈을때 한컴팀장들은 알았으라나..ㅋㅋㅋ

Quote:
ps. #include >stdio.h> 어떻게 표현하죠?

#include "stdio.h"
#include "stdlib.h"
#include "assert.h"
#include "string.h"
#include "ctype.h"
 
/*
*/
#define MAX_BUFFER		(32)
#define MAX_INPUT		(MAX_BUFFER - (1))
#define PLUS		'+'
#define MINUS		'-'
#define CIPER		(float)(3.0)
#define MAGIC_NUMBER	(float)(0.7)
 
/*
*/
typedef enum { SUCCEEDED = 0, FAILED = -1 } ERROR;
 
/*
*/
ERROR init( char* );
ERROR release( char* );
 
ERROR expr_check( const char* );
ERROR input( char*, unsigned int );
 
ERROR separate_currency( char* , const char* );
ERROR _reverse_string( char* , const char*, int );
 
int main( int argc, char* argv[] )
{
	char input_buf[MAX_BUFFER];
	char result_buf[MAX_BUFFER];
 
	init( input_buf );
	init( result_buf );
 
	input( input_buf, MAX_INPUT );
 
	if( expr_check( input_buf ) == FAILED )	{
		perror( "expression error." );
		return -1;
	}
 
	if( separate_currency( result_buf, input_buf ) == FAILED )	{
		perror( "currency convert error." );
		return -1;
	}
 
	puts( result_buf );
 
	release( result_buf );	
	release( input_buf );
 
 
	return 0;
}
 
/*
 
*/
ERROR separate_currency( char* dest, const char* src )
{
	int length = 0;
	int comma_count = 0;
	int expr_signed = 0;
	int i = 0;
	int tmp_val = 0;
 
	assert( NULL != dest && NULL != src );
 
	length = strlen( src );
 
	assert( 0 < length );
 
	/* strlen() contain '\0' */
	length--;
 
	/* signed '-', '+' */
	if( '-' == *src || '+' == *src )
		expr_signed = 1;
 
	/* ex) -123567  ( ( 4 - 1 ) / 3.0 ) + 0.7 - 1 */
	comma_count = ( ( ( length - expr_signed ) / CIPER ) + MAGIC_NUMBER ) - 1;
 
	/* ex) (-123),567  4 = 7 - ( 1 * 3 )  */
	tmp_val = length - ( comma_count * 3 );
 
	while( 0 < tmp_val-- )
	{
		*dest++ = *src++;
	}
 
	while( 0 < comma_count-- )
	{
		*dest++ = ',';
		*dest++ = *src++;
		*dest++ = *src++;
		*dest++ = *src++;
	}
 
	return SUCCEEDED;
}
 
/*
 
*/
ERROR _reverse_string( char* dest , const char* src, int length )
{
 
	assert( NULL != dest && NULL != src );
 
	assert( 0 < length );
 
	/* strlen() contain '\0' */
	length--;
 
	/* base + off */
	src += length;
 
	while( 0 <= length-- )	
	{ 
		*dest++ = *src--; 
	}
 
	return SUCCEEDED;
}
 
/*
 
*/
ERROR expr_check( const char* str )
{
	char* dummy = NULL;
	char* tmp = NULL;
	size_t len = 0;
	int i = 0;
	ERROR err = SUCCEEDED;
 
	assert( NULL != str );
 
	len = strlen( str );
 
	assert( len > 0 );
 
	tmp = (void*) malloc ( sizeof(char) * len );
 
	assert( NULL != tmp );
 
	memset ( tmp, 0, sizeof(char) * len );
 
	dummy = strncpy( tmp, str, len - 1 );
 
	assert( dummy == tmp );
 
	/* signed check */
	if( !isdigit(*dummy) && PLUS != *dummy  && MINUS != *dummy )
		err = FAILED;
 
	/* optimize zone - casting (void*)*(++dummy) -, Macro, Assemble */
	while( NULL != *(++dummy) )
	{
		/*  58 -> 127 = 69 ( alphabet ), 0 -> 47 = 48 ( specail )*/
		if( *dummy > '9' || '0' > *dummy )
			err = FAILED;
	}
 
	memset ( tmp, 0, sizeof(char) * len );
 
	free( tmp );
 
	tmp = NULL;
 
	return err;
}
 
/*
 
*/
ERROR input( char* str, unsigned int size )
{
	char* tmp;
 
	assert( NULL != str );
	assert( 0 < size && MAX_BUFFER > size );
 
	tmp = fgets( str, size , stdin );
 
	assert ( tmp == str );
 
	return SUCCEEDED;
}
 
/*
 
*/
ERROR init( char* str )
{
	assert( NULL != str );
 
	memset( str, 0, sizeof(char) * MAX_BUFFER );
 
	return SUCCEEDED;
}
 
/*
 
*/
ERROR release( char* str )
{
	assert( NULL != str );
 
	memset( str, 0, sizeof(char) * MAX_BUFFER );
 
	return SUCCEEDED;
}

Hello World.

alfalf의 이미지

shell script에서는 함수로 아래처럼...

function clarify_number {
    echo $1 | rev | sed 's/\([0-9]\{3\}\)/\1,/g; s/,$//' | rev
}

익명 사용자의 이미지

이런 코드도 괜찮지 않나요..

void add_comma(const char *str, char *buf, int buflen)
{
while(buflen > 3)
{
*buf++ = *str++;
*buf++ = *str++;
*buf++ = *str++;
*buf++ = ',';
buflen -= 3;
}

for(; buflen > 0; buflen--)
*buf++ = *str++;

*buf++ = 0;
}

익명 사용자의 이미지

에휴.. 아무생각 없이..잘못 생각했네요..

위에서 부터 3개 단위로 점이 찍히게 프로그램 됐네요.. ㅋㅋ

지워야 할건데 지울 방법을 모르겠네요...ㅠㅠ

ydongyol의 이미지

http://groups.google.co.kr/group/comp.lang.c/msg/165f11c5f832321e?

/*****************************************************************************
 *                                commify()                                  *
 *                                                                           *
 *  Commify a number, that is add commas between every third digit ahead of  *
 *  the decimal point.  Rounds off to abs(round) digits following the        *
 *  decimal point. Stores the results into the buf[] passed to the function  *
 *  and returns a pointer to it.  Uses the standard library function fcvt()  *
 *  to do the conversion from the double val to the string of digits.        *
 *                                                                           *
 *****************************************************************************/
 
char *commify(double val, char *buf, int round)
{
    static char *result;
    char *nmr;
    int  dp, sign;
 
    result = buf;
    if(round < 0)                        /*  Be sure round-off is positive  */
        round = -round;
    nmr = fcvt(val, round, &dp, &sign);  /*  Convert number to a string     */
    if(sign)                             /*  Prefix minus sign if negative  */
        *buf++ = '-';
    if(dp <= 0){                         /*  Check if number is less than 1 */
        if(dp < -round)                  /*  Set dp to max(dp, -round)      */
            dp = -round;
        *buf++ = '0';                    /*  Prefix with "0."               */
        *buf++ = '.';
        while(dp++)                      /*  Write zeros following decimal  */
            *buf++ = '0';                /*     point                       */
    }
    else{                                /*  Number is >= 1, commify it     */
        while(dp--){
            *buf++ = *nmr++;
            if(dp % 3 == 0)
                *buf++ = dp ? ',' : '.';
        }
    }
    strcpy(buf, nmr);                     /*  Append rest of digits         */
    return result;                        /*     following dec pt           */
} 

--
Linux강국 KOREA
http://ydongyol.tistory.com/

--
Linux강국 KOREA
http://ydongyol.tistory.com/

cppig1995의 이미지

사고의 확장
1. 첫 글자가 +, -인가?
2. 뒤집어 역순으로
3. 소숫점을 찾는다
4. 세 글자마다 쉼표
5. 다시 뒤집어 정순으로
라는 망상을 해봤습니다.

자 이걸 2와 5 과정을 줄여서 효율적으로 만들면 됩니다. :)



돼지군 작업실 Revision E: E-Prot, Eightbyte OS, ...
F/OSS를 위해 뭔가 하고 싶습니다. 뭘 하면 좋을까요?

Real programmers /* don't */ comment their code.
If it was hard to write, it should be /* hard to */ read.

IsExist의 이미지

memmove 버전

/** s는 충분한 공간이 있어야 한다. */
void add_comma(char *s)
{
    int n, l;
 
    if (!s) return;
    n = 3 + 1;
    l = strlen(s);
    while (l > 3) {
        l -= 3;
        memmove (s + l + 1, s + l, n);
        *(s + l) = ',';
        n += (3 + 1);
    }
}

---------
간디가 말한 우리를 파괴시키는 7가지 요소

첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스

이익추구를 위해서라면..

다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치

---------
간디가 말한 우리를 파괴시키는 7가지 요소

첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스

이익추구를 위해서라면..

다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치

M.W.Park의 이미지

오늘 따라 잠이 안오는 군요. 예전 글이지만 다시 앞으로 올라와서 한번 살펴보니
common lisp 버전이 없는 듯하여 답글 하나 추가해봅니다.

CL-USER> (format t "~:d" 123456)
123,456
NIL
CL-USER> (defun print-money (amount) (format t "~:d" amount))
PRINT-MONEY
CL-USER> (print-money 1234567890987654321)
1,234,567,890,987,654,321
NIL

common lisp은 그냥 format directives에서 지원됩니다.
장점은... 언제나 그렇듯 문제에 집중할 수 있습니다. ^^;

http://gigamonkeys.com/book/a-few-format-recipes.html

-----
오늘 나의 취미는 끝없는, 끝없는 인내다. 1973 法頂

-----
오늘 의 취미는 끝없는, 끝없는 인내다. 1973 法頂

shyblue의 이미지

뭐, C나 C++ 코드들은 워낙 잘 작성들을 해놓으셔서, 그냥 지나가다가 Harbour(Clipper)라는 언어로 구현해봅니다.

function add_comma( dest )
    if ( empty(dest) )
        return NIL
    endif
    result := rtrim( transform("999,999,999,999,999,999,999", dest) )
return result

時日也放聲大哭

時日也放聲大哭

weongyo의 이미지

man strfmon(3)

Flour의 이미지

1 while ($Num =~s/(.+)(.{3})/$1,$2/);
생각해보니 틀렸네요 다시 수정합니다.
1 while ($Num =~s/(\d+)(\d{3})/$1,$2/);

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.