김영식 교수님
kys@kpu.ac.kr

ODE (Open Dynamics Engine)의 소스를 사용함.
자세한건 www.ode.org 로..

이건 빌드
http://wsl.kpu.ac.kr/%7Ekys/index.html
출처 카페 > 게임 개발자 네트워크 (jz.. / 자존심
원본 http://cafe.naver.com/jzsdn/2284

♧ 유니코드


-싱글바이트와 더블바이트

: strlen을 호출하면 싱글바이트 문자들의 문자열 종결자(제로)배열내 문자들의 수를 리턴한다. 문제는 몇 언어들과 쓰이고 있는 시스템들 (예 : 일본어 kanji)이 싱글바이트가 제공하는 최대 256개의 심볼보다 더 많은 기술을 사용한다는 것이다. 그래서 더블바이트 문자세트는 이런 언어들과 기록시스템을 지원한다. (비주얼 C++런타임 라이브러리 : _mbslen함수)


- 1988년 애플과 제록스 사에 의해 표준화


- 더블바이트 문자세트는 다음 바이트가 같은 문자의 일부인지 새로운 문자인지 구분해야 하는데 유니코드는 그럴 필요가 없어서 CharNext, CharPrev와 같은 함수는 필요가 없다. 또한 16비트 값으로 표현하기 때문에 65000이상의 문자들을 이용할수 있으므로 싱글바이트 문자세트로 256문자들을 이용하는것과는 많은 차이가 난다.


- 현재 29000코드 포인트들이 할당되지 않고 있어서 이것들은 미래의 사용을 위하여 보류된 것이다. (키릴어, 영어, 히브리어, 아라비아어 등등을 표현한다)


- 유티코드를 사용하는 이유

        ? 언어들 사이에서 쉬운 데이터로 변환이 가능

        ? 모든 언어를 지원하는 싱글 바이너리, .exe  또는 DLL파일의 분배가 가능

        ? 애플리케이션의 효율향상


- 윈도우 2000은 유니코드 사용을 근거로 만들어졌다. 즉, 윈도우 함수를 호출하고 그것을 ANSI문자열로 넘기면, 시스템은 처음에 그 문자열을 유니코드로 바꾸고, 그 다음 유니코드 문자열을 운영체제로 넘긴다. 또한 함수로부터 ANSI문자열을 기다린다면, 애플리케이션으로 돌아가기 전에 시스템은 유니코드 문자열을 ANSI문자열로 변환한다. 물론 이런 변환을 수행하기 위해서는 시간과 메모리의 오버헤드가 존재한다.

예를들어, CreateWindowEx를 호출하고 유니코드가 아닌 클래스 이름과 윈도우 캡션을 전달하게 되면, CreateWindowEx는 메모리 블록을 할당해야 하고, 유니코드가 아닌 문자열을 유니코드로 바꿔야 한다. 그리고 그 결과를 할당된 메모리 블록에 저장하고, 유니코드버전 CreateWindowEx를 호출하는 함수를 만들어야 한다. 또한 문자열을 버퍼에 넣는 함수에서는 애플리케이션이 그 문자열을 처리히기 전에 시스템은 반드시 유니코드를 유니코드가 아닌 코드로 바꾸어야 한다. 그러므로 처음부터 유니코드를 사용하는 애플리케이션을 개발함으로써 능률적으로 수행하게 할 수 있다.


- Microsoft Unicode Story

        ? 윈도우 2000은 유니코드와 ANSI를 지원한다. 즉 둘 중 하나로 개발할 수 있다.

        ? 윈도우 98은 ANSI만 지원하므로 ANSI로만 개발해야 한다.

        ? 윈도우 CE는 유니코드만 지원하므로 유니코드로만 개발해야 한다.


- COM : 문자열을 요구하는 모든 COM인터페이스 메소드는 단지 유니코드 문자열만 받도록 되어있다. 왜냐하면 COM은 전형적으로 다른 컴포넌트가 서로 대화할 때 사용되고, 유니코드는 문자열을 전달하는 좋은 방법이기 때문이다. 만일 윈도우 98로 개발하고 COM을 사용한다면 많은 문제가 발생할 것이다.


- 유니코드 소스 작성하는 방법

? typedef unsigned short wchar_t;

wchar_t szBuffer[100]; 이렇게 버퍼를 생성한다. 물론 strcpy, strcat같은 표준 c런타임 문자열함수는 ASNI문자열만 연산한다. 그래서 그에 대응하는 유니코드함수가 있다.

char* strcat(char*, const char*);

wchar_t* wcscat(schar_t*, const wchar_t*);

이렇게 모든 유니코드 함수는 wcs(wide character string)로 시작한다. 그러므로 str을 wcs로 변경하면 된다.


- CreateWindowExW와 CreateWindowExA함수 프로토타입 비교

? PCWSTR : 상수 유니코드 문자열 포인터

? 윈도우 2000에서 CreateWindowExA의 마이크로소프트 소스코드는 단순히 청크(thunking)또는 변환, 즉 ANSI문자열을 유니코드 문자열로 바꾸기 위한 레이어가 된다.

그래서 코드는 바꾼 문자열을 전달하여 CreateWindowExW를 호출한다.


- ANSI와 유니코드를 대비한 애플리케이션 만들기

? 윈도우 문자열 함수 : lstrcat, lstrcmp, lstrcmpi, lstrcpy, lstrlen

: 이들 함수는 소스 모듈이 컴파일될 때 UNICODE가 정의되었는지에 따라 유니코드 버전 함수나 ANSI버전 함수를 호출하는 매크로로 구현된다. 즉, lstrcat는 lstrcatA와 lstrcatW로 확장될 것이다.

? 일반적인 데이터타입(TCHAR과 PTSTR과 같은)을 텍스트문자와 문자열로 사용한다.



- 유니코드는 한마디로 말하면 모든 문자를 2byte로 표현하자는 의미이다.

- 98은 전혀 지원하지 않는다.

- CE은 유니코드만 지원한다.

- 2000은 유니코드와 ANSI를 지원한다.

- 미래 os는 유니코드만 지원하게 될 거 같다.

- 그래서 코드를 재 사용할 때 문자열 표현에 문제가 생긴다.

예)char sz[100] : 미래에는 char보다 short int로 컴파일 해야한다.

그래서 지금 코딩할 때 유니코드 버전을 대비해서 미리 코딩하는 기법을 써야한다.

- 유니코드 쓰는 방법 : short int sz[100];

그런데 아직은 전부 다 이렇게 쓸 수가 없다.

- 유니코드를 쓰는 세 가지 관점

① 변수 선언

② 상수 선언

③ 함수 선언


1) 변수 선언


- char나 short int를 쓸 수 없어서 매크로를 만들어 놨다. : TCHAR

이러면 상황에 따라서 두 가지로 컴파일이 된다. 유니코드는 short int로, ansi는 char로 컴파일이 된다. 그러므로 앞으로는 TCHAR을 쓰는 게 좋다.

윈도우 CE에서 작업할 때 기존의 소스를 포팅하려면 char를 전부 수정해줘야 하는 문제가 생긴다.

- 포인터 변수일 경우는 (정수는 문제가 되지 않기 때문에 char *를 말한다) TCHAR*로 써줘야 한다.  그래서 이것도 다음과 같이 매크로로 만들어 놓았다.

- LPCTSTR : 상수형이다. c가 있으면  TCHAR* 상수형이다

- LPTSTR : TCHAR* 버퍼형이다. 즉 배열을 잡아서 써야한다. 그렇지만 LPCTSTR은 상수형이므로 바로 "HOWON"과 같이 쓸 수 있다.

-LPCSTR, LPSTR : 이것들은 T가 없으므로 여전히 char*로만 컴파일이 된다. 그래서 쓰지 않는다.


2) 상수 선언


- 문자열 상수 "   "를 말한다. "abc"면 \0까지 포함해서 4byte인데 이걸 유니코드에서

컴파일하면 여전히 4바이트이다. 그래서 유니코드 상수로 하여 L"abc"로 하면 8바이트가 된다. 그러나 이렇게 하면 지금 당장 컴파일이 안되므로 역시 매크로를 만들어 왔다.

- _T, TEXT : _T("abc") 이렇게 쓰면 컴파일될 때 현재 상황은 "abc"가 되고 유니코드로 컴파일하면 L"abc"가 된다.

TEXT는 API용이고 _T는 MFC용이다.


3. 함수사용


- strcpy는 char*를 인자로 받는다. 그래서 유니코드에서는 이 함수를 쓰지 못한다.

그래서 유니코드용 문자열 복사 함수를 만들어 놓았다. wcscpy(short int*  )

그런데 지금 코딩할 때는 이걸 쓸 수 없으므로 역시 매크로를 만들어 놓았다. : _tcscpy

이 함수는 지금 컴파일 하면 strcpy가 되고 나중에는 wcscpy가 된다.

strcat, strlen등도 마찬가지이다. 즉, _tcslen, _tcscpy 이렇게 _tcs만 앞에 붙인다.

그런데 atoi함수같은 경우는 _ttoi, sprintf는 _stprintf이다.

즉 간혹 두 번 째에 t가 나올 경우가 있다.

- MSDN찾는 방법

strcpy : 위는 ansi, 두번째는 유니코드용, 밑에 TCHAR Routine에 _tcscpy가 나온다.

- API에 CreateWindow가 있다면 CreateWindowA는 ANSI용이고 CreateWindowW는 유니코드용이다. 이렇게 두개 함수가 있으므로 그냥 사용하면 된다.

- PTSTR과 LPTSTR은 같은 의미이다. 윈3.1때는 포인터에 NearPointer 2바이트가 있었고, 4바이트짜리 포인터가 있었다. 그래서 long형이 아닌 걸 만들어 놓았다.

지금은 세그먼트가 없으므로 LPCTSTR이나 PCTSTR차이가 없다. 그래서 PTSTR은 사용하지 않는다.

- char temp sz[100];

  for(i = 0; i<sizeof(sz); i++ )

이렇게 하면 틀린다. 왜냐하면 유니코드가 아니면 100이므로 괜찮지만 유니코드에서는 short int로 바뀌므로 200이 된다. 그래서 sizeof(sz)/sizeof(TCHAR)로 해줘야 한다.



♣ 윈도우 문자열 함수



- CompareString : 문자열 비교하는 api함수이다. c 런타임함수로 strcmp함수가 있으니까 이걸 쓰거나 _tcscmp를 쓰면 되는데 이 함수는 문법적으로는 유니코드를 지원하지만 논리적으로는 지원하지 않는다.

유니코드는 한글, 알파벳이 한꺼번에 모여있다. 그래서 같은 국가문자들끼리만 비교해주려면 CompareString함수를 써야한다. CompareString함수의 첫 번째 인자가 지역ID를 나타낸다. 즉, 이 인자를 사용해서 문자의 EMt을 검사함으로서 두 문자열을 비교하게 된다. 이런 동작은 단순히 숫자를 비교하는 C런타임함수보다 훨씬 의미가 더 있다. (C런타임함수 strcmp, wcscmp등은 문자열에서 코드 포인트의 값을 비교한다. 즉, 함수는 실제 문자의 의미를 무시하고 단순히 각 문자의 숫자 값을 검사한다.)

- 즉 문자열 대소비교할 때 같은 버퍼 안에 한글, 영문이 섞여있을 때는 이  API함수를 써야한다.

- CharLower, CharUpper함수들은 ansi가 없고 유니코드 전용함수들이다.

- 이런 함수들을 쓸때는 다음과 같이 코딩한다.


#ifdef _UNICODE

        CharLower();

#else

        tolower();

#endif


- IsTextUnicode : 텍스트가 ANSI인지 유니코드인지 결정하는 함수로서 참, 거짓을 반환한다. 즉, 버퍼가 유니코드인지 ANSI인지 판단해주는 함수이다.

PDA는 윈도우CE를 쓴다. 메모장에서 작성한걸 pda에서 쓰려면 먼저 유니코드로 변환해야 하는데 이럴 때 이 함수를 써서 유니코드인지 아닌지를 먼저 판단한다.

- DWORD IsTextUnicode(CONST PVOID pvBuffer, int cb, PINT pResult);

- 함수인자로는 첫 번째 인자는 비교하고자 하는 버퍼주소인데 이때 버퍼문자열이 유니코드 문자열인지 ANSI 문자열인지 알지 못하므로 void포인터형이다.

두 번째 인자는 버퍼 바이트 수인데 역시 버퍼내용을 모르기 때문에 문자 카운트가 아닌 바이트의 카운트가 된다. 세 번째 인자는 비교하는 방법(옵션인데 null을 준다)으로 되어있다.

특히 이 함수는 통계적으로 판단한다. 즉 버퍼에 유니코드가 더 많으면 유니코드라고 판단해준다.


♧ 유니코드와 ANSI간의 문자열 변환


- MultiByteToWideChar : ANSI를 유니코드로 변환

- WideCharToMultiByte : 유니코드를 ANSI로 변환

- com은 os와 상관없이 반드시 유니코드를 써야한다.

- .Net : 실행가능한 개체를 만들자는게 목표이다. 실행가능한 개체란 소스상태가 아니고

컴파일된 상태를 말한다. 그래서 com도 같은 목표를 가지고 있다.

1절. 함수포인터란 ?

C 에서의 함수포인터는 언어와 관련된 문법적인 내용임으로 함수포인터에 대한 설명은 지극히 의례적인 내용이 될수 있겠지만, 꽤 복잡하기도 하고 재미있게 사용할수도 있으므로 굳이 강좌를 만들었다.

함수 포인터는 말그대로 함수의 위치를 가리키는 포인터이다.

C 언어의 경우 함수자체를 변수로 만들수는 없다. 대신 함수를 포인트하는 것은 가능한데, 이것을 통해서 함수를 포인터 처럼 사용할수 있으며, 이 포인터가 가르키고 있는 곳의 함수를 실행시킬수도 있다.


1.1절. 선언방법

포인터는 하나의 자료형이므로 포인터가 가르키는 데이타의 타입정보를 이용해서 포인터를 선언해줘야 한다. 물론 함수포인터의 경우 포인터하는 대상이 데이타가 아니고 함수라는 점이 다르긴 하지만.. 말이다.

다음은 함수포인터를 선언하기 위한 전형적인 방법이다.

return_type (*function)(arg1, arg2, ...);
			
이해하기 쉽게, 만약 int hello(char *) 라는 함수를 가리키는 함수포인터를 선언하고자 한다면 아래와 같이 하면 된다.
int (*func_name)(char *);
			
다음은 간단한 예제이다.

funcpter.c

#include <stdio.h>
void hello(char *name)
{
    printf ("Hi %s\n", name);
}

int main()
{
    void (*Func)(char *);
    Func = hello;
    Func("test");
}
			
위 코드를 이해하는 데에는 별 무리가 없을것이다. void 리턴타입을 가지고 인자로 캐릭터 포인터를 가지는 함수포인터를 사용하고 있음을 알수 있다.


1.2절. 왜 함수포인터를 사용하는가

익히 경험해서 알고 있겠지만, C 에서의 포인터는 잘못 사용하면 악몽이 될수 있다. 특히 함수포인터를 선언해서 쓸 경우는 그 독특한 문법과 익숙치않은 포인터개념의 짬뽕으로 인하여 코드자체가 암호문처럼 변할수도 있다. 이것은 프로그램의 유지/보수를 어렵게 할수도 있다.

그러나 그럼에도 불구하고 제대로 사용할경우 그 장점을 활용하여 유용한 프로그래밍 기법으로 활용할수 있는데, 바로 generic한 함수(혹은 알고리즘)의 작성을 가능하게 한다는 점이다. 또한 잘만 사용하면 오히려 유지/보수가 수월하게끔 만들수도 있다. 다음장에서는 간단한 예제를 이용해서 함수포인터 의 장점을 활용하는 방법에 대해서 알아보도록 하겠다.


2절. 함수포인터의 활용

이번장에서는 함수포인터의 활용방법에 대해서 몇가지 예를 들어서 알아보도록 하겠다.


2.1절. Generic 함수(알고리즘)의 작성

각 학생의 과목별 성적데이타가 있고, 과목별 최고 점수를 가져오는 프로그램이 있다고 가정하자. 처음에 이 프로그램은 각 과목중 "국어" 최고 점수만을 가져오도록 만들었다. 그런데 "수학" 최고 점수만을 가져오도록 변경하고 싶으면 어떻게 해야 할까? 혹은 최고 평점을 가져오기를 원할수도 있을것이다. 물론 각각의 경우에 대해서 별도의 이름을 가지는 함수를 만들면 되기는 하겠지만 이러한 경우에 함수포인터를 이용하면, 좀더 유지보수가 쉬운 깔끔한 코드를 만들어 낼수가 있다.

max.cc

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <vector>

using namespace std;

typedef struct _pinfo
{
    char name[12];  
    int  math;
    int  korean;
    int  eng;
} pinfo;

void printmax(vector<pinfo> va, void (*SortFunc)(vector<pinfo>))
{
    printf("최고 성적 출력 프로그램\n");
    SortFunc(va);
}   

void engmax(vector<pinfo> va)
{
    int max = 0;
    int index = 0;
    for (int i = 0; i < va.size(); i++)
    {
        if (va[i].eng > max)
        {
            max = va[i].eng;
            index = i;
        }
    }
    printf("영어 최고점수 획득자는 %s : %d\n", 
        va[index].name,
        va[index].eng);
};


int main()
{
    pinfo myinfo;
    vector<pinfo> va;

    void (*Sort)(vector<pinfo>);

    myinfo.korean = 80;
    myinfo.eng    = 65;
    myinfo.math   = 99;
    strncpy(myinfo.name, "yundream", 12);
    va.push_back(myinfo);

    myinfo.korean = 90;
    myinfo.eng    = 65;
    myinfo.math   = 74;
    strncpy(myinfo.name, "kknd", 12);
    va.push_back(myinfo);

    myinfo.korean = 63;
    myinfo.eng    = 88;
    myinfo.math   = 55;
    strncpy(myinfo.name, "junny", 12);
    va.push_back(myinfo);

    printmax(va, engmax);
}
			
위의 코드를 보면 printmax 라는 함수가 있는데, 함수포인터를 인자로 넘겨 받음으로써, 좀더 제너릭하게 확장시킬수 있도록 만들어져 있다. 위의 경우는 영어최고점자의 정보를 출력시키도록 해놓았는데, 만약 최고 평점자에 대한 정보를 출력 시키기를 원한다면, 다음과 같은 함수를 만들고, printmax 에 인자로 넘기면 될것이다.
void avgmax(vector<pinfo> va)
{
    int max   = 0;
    int total;
    int index = 0;
    for (int i = 0; i < va.size(); i++)
    {
        total = (va[i].eng + va[i].math + va[i].korean);
        if ( total > max)
        {
            max = total;
            index = i;
        }
    }
    printf("최고평자 점정보 : \n");
    printf("이름 : %s\n", va[index].name);
    printf("영어 : %d\n", va[index].eng);
    printf("국어 : %d\n", va[index].korean);
    printf("수학 : %d\n", va[index].math);
    printf("평점 : %.2f\n", (float)max/3.);
}
			
아래와 같이 printmax 를 호출하면 된다.
printmax(va, avgmax);
			
이처럼 함수포인터를 직접 넘김으로 인해서, 프로그래머는 필요에 따라 원 쏘쓰의 큰 변화없이 자기가 필요로 하는 코드만 만들어서 쉽게 확장시킬수 있다.


2.2절. 그외의 활용

여기에서는 이러한 용도로도 사용이 가능하다는걸 보여주는 팁수준의 활용용도를 보여줄것이다.

필자가 쓰레드 프로그래밍을 할때 가끔 사용하는 방법인데, 코드를 훨씬 깔끔하고 보기 쉽게 만들어준다. 포인트는 쓰레드 함수를 함수포인터를 값으로 하는 vector 에 등록시켜서 쓰레드 생성등에 사용하는 방법이다.

이방법을 쓰면 비록 몇줄이긴 하지만 분명히 코딩량을 줄일수 있다. 그리고 쓰레드 함수를 한영역에서 모아서 관리함으로 코드를 좀더 보기 쉽게 만들어준다.

funcpt_tip.cc

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <vector>
#include <iostream>

#define MAX_THREAD_NUM 20

using namespace std;

void *func1(void *)
{
    printf("Thread 1\n");
    pause();
}
void *func2(void *)
{
    printf("Thread 2\n");
    pause();
}
void *func3(void *)
{
    printf("Thread 3\n");
    pause();
}
void *func4(void *)
{
    printf("Thread 4\n");
    pause();
}

int main()
{
    // 인자가 함수포인터인 vector 생성
    vector<void *(*)(void *)> thread_list;
    vector<pthread_t> tident(MAX_THREAD_NUM);
    int status;

    thread_list.push_back(func1);
    thread_list.push_back(func2);
    thread_list.push_back(func3);
    thread_list.push_back(func4);

    cout << "등록된 쓰레드 " << thread_list.size() << endl;
    for (int i = 0; i < thread_list.size(); i++)
    {
        pthread_create(&tident[i], NULL, thread_list[i], (void *)NULL);
    }

    cout << "thread Join Wait" << endl;
    for (int i = 0; i < tident.size(); i++)
    {
        pthread_join(tident[i], (void **)&status);
    }
    return 1;
}
			


3절. 결론

이상 간단하게 함수포인터의 사용방법에 대해서 알아보았다. 함수포인터는 몇몇경우에 유용하게 사용할수 있지만, 잘못사용하게 될경우 대단히 유지보수가 어려운 코드를 만들어 낼수도 있다. 위의 함수포인터의 선언을 보면 알겠지만, 결코 익숙한 선언및 사용방법이 아니기 때문이다. 또한 포인터라는 개념자체가 혼동을 줄수 있기 때문이다.

어쨋든 사용방법은 알아두는게 좋다. 위에서는 예로 들지 않고 있지만, C 에서 객체지향을 흉내내기 위한 목적으로써, 함수포인터를 사용하는 경우도 많기 때문이다.


출처 : http://www.joinc.co.kr/modules.php?name=News&file=article&sid=119

출처 : Tong - ♡Hopi♡~님의 ♡ C/C++ ♡통

'C/C++언어' 카테고리의 다른 글

printf 문을 만들어보자  (0) 2008.01.28
여러 C++ 컴파일러  (0) 2007.09.06
함수포인터란 ?  (0) 2007.09.06
[본문스크랩] 파일 입.출력  (0) 2007.09.06
[본문스크랩] VC++ Article  (0) 2007.09.06
[강좌] MASM 6.0 사용법  (0) 2007.09.06
 

파일 입.출력


파일 입.출력은 앞으로도 매우 중요하게 쓰이는 부분이다.

어떤 파일로부터 데이터를 읽어들이고 또 데이터를 어떤 파일에 저장하는 것을 말한다.


데이터 저장과 불러오기 기능을 구현한것

읽기 / 쓰기 전용 , 읽기 / 쓰기 혼합 모드 기능

파일을 열고 닫는 작업이 쌍으로 존재한다.


[파일 오픈]


* FILE *fopen(const char *filename, const char *mode) ;    // 성공시 해당파일의 포인터. 실패시 NULL 포인터 리턴


- filename : 문자형 포인터로 사용할 파일 이름을 지정

- mode     : 파일에 대한 접근 방식

- 리턴값     : open 한 파일을 가리키는 파일 포인터


mode 에는 기본적으로 r, w, a 가 있고 세부적으로 바이너리 코드(이진모드)와 텍스트 모드로 분리



[파일 오픈 모드]


r   텍스트 모드이며 읽기 모드로 파일을 오픈

w  텍스트 모드이며 쓰기 모드로 파일을 오픈. 단 파일을 없으면 자동으로 생성. 있으면 내용을 전부 삭제하고 새로운 파일을 생성

a   텍스트 모드이며 쓰기 모드로 파일을 오픈. 단 파일이 없으면 자동으로 생성. 있으면 파일의 가장 끝부분에 이어 쓰기를 실행

b   바이너리 모드를 의미

+   읽기 / 쓰기의 혼합 모드가 가능함을 의미



[데이터 입.출력 모드]


t   텍스트 모드(text mode)

b   2진 모드(binary mode)


데이터의 읽기 / 쓰기를 텍스트 모드로 할건지 2진 모드로 할건지 정해주는 거이다.

문자열과 같은 텍스트 기반의 데이터는 텍스트 모드로 입. 출력 하는 것이 좋고 데이터 변환이 발생하면 안되는 경우 2진 모드로 데이터 입. 출력 하는 것이 좋다.


파일 접근 모드 + 데이터.입출력모드 = 파일 개방 모드     rt  ( r + t )  : 텍스트 모드의 읽기 전용



[일반적인 파일 열기 방법과 파일 오픈시 주의 사항]


FILE *fp ;                                                 // 파일을 오픈하기전에 반드시 파일포인터 선언

.......

fp = fopen( "test.txt" , "r" ) ;                        // 파일이름과 모드는 "와" 사이에 입력   test.txt 파일을 읽기 모드로 읽는 것이다.  

.......                                                        // 반드시 test.txt 파일이 폴더내에 있어야 한다.

if( fp == NULL )                                          // 파일이 제대로 열렸는지 반드시 체크

{

     ........

     return -1 ;

}



[파일 종결]


* int fclose(FILE *stream) ;


- stream : fopen() 함수를 통해 생성된 파일 포인터

- 현재 열린 파일을 닫을때 사용

- 출력 버퍼에 남아있는 데이터를 파일에 기록한 후 파일을 닫음


FILE *fp ;

.......

fp = fopen( "test.txt", "r" ) ;             // 파일 오픈

.......

fclose( fp ) ;                                  // 파일 닫음


동적할당에서 malloc 과 free 가 짝이 듯이 항상 파일을 오픈하면 종료를 해주어야 한다. fopen -> fclose



* 파일을 읽기 와 쓰기 에서 사용되는 함수들은 기존의 함수와 비슷하다. f 만 들어가면 된다.















<fputc() 함수>


하나의 문자를 파일에 저장

int fputc(int c, FILE *fp) ;

- c : 파일에 저장할 데이터

- fp : open 한 파일을 가리키는 포인터

- 리턴값 : fputc() 함수가 파일에 기록한 데이터


#include <stdio.h>

void main()

{

     FILE *fp ;

     fp = fopen( "text.txt", "w" ) ;                  // w 생성한다. test.txt   기존에 파일이 있으면 데이터 삭제 새로 사용.

     for( int i = 0 ; i < 128 ; i++ ){

          fputc( '\n', fp ) ;                           

          fputc( i, fp ) ;

     }

     fclose( fp ) ;

}


실행하면 아무것도 안나올 것이다.. 폴더에 가보면 test.txt 란 파일이 생기고  아스키 코드값 0번부터 128번의 문자들이 들어있다.


<fgetc() 함수>


* int fgetc( FILE *fp ) ;

- fp : fopen() 함수를 통해 생성된 파일 포인터

- 리턴값 : 문자

- fp 는 항상 NULL 이 될수 없음


폴더에 text.txt 안에 a b c 를 작성하고 종료.(저장. 공백 주의)


#include <stdio.h>

#include <stdlib.h>


void main()

{

     FILE* fp ;

     fp = fopen( "test.txt" , "r" ) ;

     if( fp == NULL){

          printf("Error\n") ;

          exit( 1 ) ;

     }

    

     while( !feof(fp) )                                  // 끝이 아니라면 실행

          printf( "%c", fgetc(fp) ) ;                   // 문자를 읽어들여 출력  a b c 출력

     

     fclose( fp ) ;

}


<feof() 함수>


* int feof( FILE *filepointer )

- fileopinter : fopen() 함수를 통해 생성된 파일 포인터

- 현재 가리키는 위치가 파일의 끝인가를 검사한다.

- 리턴값 : 현재 가리키는 위치가 파일의 끝인 경우 0 을. 아닌 경우에는 0 이 아닌 값을 리턴


<fputs() 함수>


문자열을 대상파일( 지정한 파일 포인터의 파일) 에 기록


* int *fputs( const char *buffer, FILE *fp ) ;

- buffer : 출력할 문자열의 포인터

- fp : 출력할 파일을 가리키는 포인터


#include <stdio.h>

#include <stdlib.h>


void main()

{

     FILE *fp ;

     fp = fopen( "test.txt", "w" ) ;


     fputs( "Hello" , fp ) ;                              // test.txt 에 Hello 가 저장된다.

     fclose( fp ) ;

}


<fgets() 함수>


파일 포인터로부터 원하는 양 만큼의 문자열 데이터를 읽어옴


* char *fgets( char *buffer, int n, FILE *fp ) ;

- buffer : 문자열을 저장하게 되는 영역

- n : 읽어드릴 양

- fp : open 한 파일을 가리키는 포인터


현재 test.txt 파일에는 Hello 가 저장되어 있다.


#include <stdio.h>

#include <stdlib.h>


void main()

{

     FILE *fp ;

     char str[20] ;

     fp = fopen( "test.txt", "r" ) ;

   

     fgets( str, sizeof(str), fp ) ;

     printf( "str : %s\n", str ) ;                     // Hello 출력

     fclose( fp ) ;

}


<fread() 함수>


파일로 부터 바이트 단위로 데이터를 읽어들인다.


* size_t fread( void *buffer, size_t size, FILE *fp ) ;

- buffer : 읽어올 데이터를 저장할 메몰리 영역의 포인터

- size : 읽어올 사이즈

- fp : 파일 포인터

- 파일 포인터 fp 에서 size 만큼의 데이터 n 개를 buffer 에 저장


<fwrite() 함수>


파일에 바이트 단위로 데이터 기록


* size_t fwrite( void *buffer, size_t size, size_t n, FILE *fp ) ;

- buffer : 파일에 기록할 데이터가 저장되어 있는 메모리 영역의 포인터

- size : 개별적인 데이터 항목의 크기

- n : 기록할 수

- fp : 파일 포인터

- buffer 에 있는 size 만큼의 데이터 n 개를 파일 포인터 fp 에 기록


#include <stdio.h>

#include <stdlib.h>


struct ADDS{

     char name[30] ;

     char number[30] ;

} ;


void main()

{

     FILE *fp ;

     int input, cnt ;

     ADDS *Juso ;


     while(1)

     {

          printf( "* [1] 입력  [2] 출력  [3] 종료 *\n" ) ;

          printf( "입력  : " ) ;

          scanf( "%d", &input ) ;


          if( input == 3 ){

               printf( "-Good Bye-\n" ) ;

               exit( 1 ) ;

          }

          if( input == 1 ){

               printf( "총 몇명 의 주소록을 입력할까요 ?" ) ;

               scanf( "%d", &cnt ) ;


               Juso = (ADDS*)malloc(sizeof(ADDS)*cnt) ;         

               fp = fopen( "test.txt", "w" ) ;


               for( int i = 0 ; i < cnt ; i++ ){

                    printf( "이름 : " ) ;

                    scanf( "%s", Juso[i].name ) ;

                    printf( "전화번호 : " ) ;

                    scanf( "%s", Juso[i].number ) ;

               }

               fwrite( &cnt, sizeof(int), 1, fp ) ;                       // fwrite cnt 변수값

               fwrite( Juso, sizeof(ADDS), cnt, fp ) ;                // fwrite Juso 구조체

               printf( "저장 완료\n" ) ;

               fclose( fp ) ;

               free( Juso ) ;

          }

          if( input == 2 ){

               fp = fopen( "test.txt", "r" ) ;            

               fread( &cnt, sizeof(int), 1, fp ) ;

          

               Juso = (ADDS*)malloc(sizeof(ADDS)*cnt)  ;          

               fread( Juso, sizeof(ADDS), cnt, fp ) ;

               printf( "\n" ) ;

     

               for( int i = 0 ; i < cnt ; i++ )

                    printf( "이름 : %s  전화 : %s\n", Juso[i].name, Juso[i].number ) ;


               printf( "\n" ) ;

               free( Juso ) ;

               fclose( fp ) ;

          }

     }

}


간단한 주소록 프로그램이다.  소스가 그다지 어렵지 않기 때문에 쉽게 이해 할수 있을 것이다.


<fprintf 와 fscanf>


fprintf 와 fscanf 함수는 printf 와 scanf 함수와 똑같다. 단지 파일이냐 모니터냐의 차이이다.  그리고 앞에 FILE 포인터 변수가 들어간다.


#include <stdio.h>

#include <stdlib.h>


void main()

{

     FILE *fp ;

     int Index ;    

     int Data ;


     fp = fopen( "test.txt", "w" ) ;

     for( Index = 0 ; Index < 9 ; Index++ )                            // 0 부터 9 까지 test.txt 에 저장

          fprintf( fp, "%d\n", Index ) ;


     fclose( fp ) ;


     fp = fopen( "test.txt", "r" ) ;

     for( Index = 0 ; Index < 9 ; Index++ ){                           // test.txt 에 있는 값을 9개 불러옴.. 0 부터 9 까지 출력

          fscanf( fp, "%d", &Data ) ;                            

          printf( "%d\n", Data ) ;

     }


     fclose( fp ) ;

}

'C/C++언어' 카테고리의 다른 글

여러 C++ 컴파일러  (0) 2007.09.06
함수포인터란 ?  (0) 2007.09.06
[본문스크랩] 파일 입.출력  (0) 2007.09.06
[본문스크랩] VC++ Article  (0) 2007.09.06
[강좌] MASM 6.0 사용법  (0) 2007.09.06
어셈블리 초급 기초 #1(정태식님)  (0) 2007.09.06

+ Recent posts