파일입출력
1. CFile 클래스
디스크의 파일로부터 데이터를 입력받거나 파일로 데이터를 출력하는 방법에는 여러 가지가 있지만 어떤 방법이든지 내부적으로는 파일핸들(File Handle)을 통한 입출력방법을 사용합니다.
디스크의 물리적인 파일을 대표하는 핸들이라는 정수 값을 하나 만든 후 이 핸들에 입출력동작을 가함으로써 파일 입출력을 수행하는 방법입니다.
MFC에서는 파일 입출력을 위해 CFile 클래스를 사용하며 이 클래스도 파일 핸들 입출력방법을 클래스 라이브러리에 맞게 잘 포장해 놓은 것에 불과합니다.
파일 핸들을 사용하여 파일을 입 출력하는 절차는 열기, 입출력, 닫기의 세가지 단계로 나누어지며, MFC의 CFile 클래스를 사용하는 절차도 이와 유사합니다.
파일을 연다(Open)는 것은 디스크에 있는 물리적인 파일과 연결되는 핸들을 준비함으로써
파일 입출력을 준비하는 과정이며 입출력은 파일에 저장되어 있는 정보를 읽어오거나 반대로 쓰는 동작입니다.
그리고 닫기(Close)는 입출력이 끝난 파일의 핸들을 반납함으로써 파일 입출력에 사용하던
메모리를 해제하는 동작입니다.
예제)
1. 파일 저장하기
1) SDI 옵션으로 프로젝트 생성
2) View 클래스에서 ID_FILE_SAVE의 COMMAND 이벤트를 선택해서 소스를 작성합니다.
CFile MyFile;
MyFile.Open(TEXT("FileTest.txt"),CFile::modeCreate | CFile::modeWrite);
MyFile.Write(TEXT("Hello MFC"),18);
MyFile.Close ();
먼저 CFile 클래스의 객체 MyFile을 선언하고 Open 함수로 파일을 엽니다.
virtual BOOL Open( LPCTSTRlpszFileName, UINTnOpenFlags, CFileException* pError = NULL);
첫 번째 인수 lpszFileName에 열고자 하는 파일의 이름을 문자열로 지정하되 필요할 경우 드라이브와 디렉토리 등의 경로명을 포함시킬 수 있습니다.
경로명이 생략될 경우는 현재 디렉토리에서 파일을 찾습니다.
두 번째 인수 nOpenFlags는 파일의 액세스 모드를 설정합니다.
액세스 모드에 modeRead가 있으면 읽기 전용으로 파일을 열고, modeWrite가 있으면 쓰기 전용으로 파일을 열고, modeReadWrite가 있으면 읽기와 쓰기가 모두 가능하도록 파일을 개방합니다.
modeCreate 플래그는 파일이 없을 경우 파일을 생성시키도록 합니다.
위 코드에서Open 함수에 의해 FileTest.txt 라는 새로운 파일이 만들어집니다.
Open 함수의 세번째 인수는 예외를 처리하는 예외 오브젝트의 포인터인데 생략 시 디폴트 값인 NULL이 적용됩니다.
Open 함수에 의해 파일이 열리고 이 파일을 가리키는 파일 핸들이 만들어 집니다.
파일 핸들은 CFile::m_hFile이라는 멤버 변수에 내부적으로 저장되고 입출력 함수들이 알아서 이 핸들을 사용하므로 사용자는 굳이 핸들에 대해 신경쓰지 않아도 됩니다.
virtual void Write( const void* lpBuf, UINT nCount);
Write함수의 첫 번째 인수가 출력할 데이터가 있는 시작번지이며 두 번째 인수가 출력할 데이터의 길이입니다.(2008의 경우 유니코드를 사용하므로 문자 수에 2를 곱해서 계산)
2. 파일 읽기
View 클래스의 ID_FILE_OPEN 이벤트를 열어서 작성합니다.
TCHAR str[20];
CFile MyFile(TEXT("FileTest.txt"),CFile::modeRead);
MyFile.Read(str ,20);
CClientDC dc(this);
dc.TextOut(0,0,str,9);
CFile 클래스의 생성자로 FileTest.txt 파일을 열고 이번에는 파일을 읽기 위해 여는 것이므로 modeRead 플래그만 사용하면 됩니다.
파일을 개방한 후 Read 함수로 파일의 내용을 읽어 들이면 됩니다.
virtual UINT Read( void* lpBuf, UINT nCount );
Read 함수의 첫 번째 인수는 파일로부터 읽어 들인 데이터를 저장할 버퍼의 포인터이며, 두
번째 인수는 이 버퍼의 최대길이입니다.
읽어 들인 데이터를 TextOut 함수를 사용하여 화면으로 출력합니다.
2. 직렬화(연속화)
프로그램이 만든 데이터를 보관하는 것을 흔히 저장(Save)이라고 하고 보관한 데이터를 다시 불러오는 작업을 복구(Load)라고 하는데, 비주얼C++ 에서는 이 두 과정을 합쳐서 직렬화(연속화(Serialization))라고 표현합니다.
데이터를 저장하고 불러오는 직렬화 작업은 많은 절차를 거쳐야만 합니다.
파일 이름을 입력 받아야 하고 입력 받은 파일을 열고 데이터를 읽어서 메모리에 넣어야 하는 기본 과정 외에도 수 많은 중간 과정들이 필요합니다.
위의 프로젝트에서 Doc 클래스에 가면 아래와 같은 코드가 있을 것입니다.
void CFileTestDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: 여기에저장코드를추가합니다.
}
else
{
// TODO: 여기에로딩코드를추가합니다.
}
}
이 함수가 파일 입출력을 수행하는 핵심함수입니다.
프레임워크가 자동으로 만들어 준 Serialize 함수는 아직 코드가 작성되어 있지 않은 빈 함수입니다.
하지만 프레임워크는 입출력이 필요할 경우 입출력에 필요한 모든 환경을 만든 후 이 함수를 호출해 주도록 준비를 하고 있습니다.
이 함수에 우리가 원하는 입출력 동작만 기술해 주면 나머지는 프레임워크가 알아서 수행해 주도록 되어 있습니다.
예제 프로젝트 생성
1. SDI 옵션으로 프로젝트 생성하는데 확장자를 test라고 입력합니다.
2. Doc 클래스에 문자를 저장할 변수 선언
CString str;
3. View 클래스의 WM_CHAR 메시지에 키보드 입력을 받아 str에 저장하는 코드 작성
CFileSerializeDoc* pDoc = GetDocument();
CString msg;
msg.Format(TEXT("%c"),nChar);
pDoc->str += msg;
Invalidate(TRUE);
4. View 클래스의 OnDraw에서 사용자가 입력한 문자열을 화면으로 출력하도록 작성
int length;
length=pDoc->str.GetLength();
pDC->TextOut (0,0,pDoc->str,length);
5. 기존의 내용을 삭제하는 코드를 작성해야 합니다.
편집하던 데이터를 삭제하는 함수는 CDocument::DeleteContents입니다.
이 함수가 하는 일을 정확하게 표현하면 Document 객체의 데이터를 삭제하는 일입니다.
이 함수는 다음 세가지 경우에 프레임워크가 직접 호출하도록 되어 있습니다.
①File/New 명령으로 문서의 내용을 지우라고 명령 했을 때
②File/Open 명령으로 새로운 데이터를 읽어 들이려 할 때
③도큐멘트 클래스가 파괴되기 직전
함수에 아래와 같은 내용을 작성합니다.
str = TEXT("");
6. 내용을 저장하는 코드를 작성합니다.
Doc 클래스의 Serialize 함수에 작성하면 됩니다.
if (ar.IsStoring())
{
ar << str;
// TODO: 여기에저장코드를추가합니다.
}
else
{
ar >> str;
// TODO: 여기에로딩코드를추가합니다.
}
ar 객체는 디스크의 파일을 대표하는 객체이며 이 객체로 데이터를 보내주면 저장하는 것이되고 이 파일에서 데이터를 가져오면 읽어오는 것입니다.
데이터를 보낼 때는 << 연산자를 사용하며 읽을 때는 >> 연산자를 사용합니다.
이 함수가 호출되었을 때 저장을 위해 호출된 것인지 읽기를 위해 호출된 것인지를 알려면ar객체의 IsStoring 함수를 호출해보면 됩니다.
이 함수는 Serialize 함수가 저장을 위해 호출되었을 경우 TRUE를 리턴해 주며 읽기를 위해
호출되었을 경우 FALSE를 리턴해 줍니다.
7. 저장플래그
사용자가 데이터를 편집한 후 저장하지 않고 프로그램을 종료하려고 할 경우 저장이 안되었음을 경고해주는 기능입니다.
View 클래스의 OnChar 함수의 하단에 아래와 같은 코드를 추가합니다.
CFileSerializeDoc* pDoc = GetDocument();
CString msg;
msg.Format(TEXT("%c"),nChar);
pDoc->str += msg;
pDoc->SetModifiedFlag(TRUE);
Invalidate(TRUE);
이제는 저장하지 않고 프로그램을 종료하려고 하면 대화상자가 화면에 출력되게 될 것입니다.
8. 직렬화가 가능한 데이터 형
분류 |
데이터형 |
정수 |
WORD, DWORD, BYTE, LONG |
실수 |
double, float |
클래스 |
CSize, CRect, CPoint, CTime, CTimeSpan, CString |
포인터 |
CObject* |
3. CObList
1) 주요 멤버 함수
CObList(int nBlockSize = 10);
POSITION AddHead(CObject* newElement );
POSITION AddTail(CObject* newElement );
리스트의 앞쪽(또는 뒤쪽)에 요소를 추가합니다.
인수로 추가하고자 하는 요소의 포인터를 넘겨줍니다.
비어있는 리스트에도 요소를 추가 시킬 수 있으므로 CObList객체를 선언한 후 바로 이 함수를 불러도 됩니다.
이 함수들은 요소를 추가한 후 추가된 요소의 위치를 나타내는 POSITION값을 리턴합니다.
POSITION 값은 리스트 내의 요소 위치를 나타내는 값이며 포인터 변수라고 생각하면 됩니다.
실제로 POSITION은 구조체 포인터로 정의되어 있습니다.
POSITION GetHeadPosition( ) const;
리스트를 순회하기 위해 처음 위치를 찾습니다.
이 함수로 리스트의 헤더 위치를 찾은 후 GetNext함수로 리스트를 순회하며 값을 읽어냅니다.
CObject* GetNext( POSITION& rPosition ) const;
CObject* GetPrev( POSITION& rPosition ) const;
rPosition이 가리키는 요소를 구하고 rPosition을 다음 요소(또는 앞 요소)를 가리키도록
이동시킵니다.
리스트의 끝(또는 처음)에 도달하면 NULL을 리턴해 줍니다.
int GetCount( ) const;
리스트 내의 요소 개수를 구합니다.
BOOL IsEmpty( ) const;
리스트가 비어있는지 조사하며 비어있을 경우 0이 아닌 값을 리턴해 줍니다.
CObject* RemoveHead( );
헤더의 요소를 삭제합니다.
리스트 내의 노드만 삭제할 뿐이며 포인터가 가리키고 있는 오브젝트가 삭제되는 것은 아닙니다.
노드를 삭제한 후 삭제된 노드가 가리키고 있던 오브젝트의 포인터를 리턴해 줍니다.
1. SDI 옵션으로 프로젝트를 생성합니다.
2. View 클래스에 변수 선언
CPoint m_nowP,m_oldP;
BOOL m_bPaint;
3. View 클래스의 WM_LBUTTONDOWN에 작성
m_bPaint =TRUE;
m_nowP=point;
m_oldP=point;
4. View 클래스의 WM_LBUTTONUP에 작성
if (m_bPaint==FALSE)
return;
m_bPaint =FALSE;
CClientDC dc(this);
dc.MoveTo(m_nowP);
dc.LineTo(point);
5. View 클래스의 WM_MOUSEMOVE에 작성
CClientDC dc(this);
if (!m_bPaint) return;
dc.SetROP2(R2_NOT);
dc.MoveTo(m_nowP);
dc.LineTo(m_oldP);
dc.MoveTo(m_nowP);
dc.LineTo(point);
m_oldP=point;
6. Doc 클래스에 변수 선언
CObList m_arLine;
7. View 클래스의 WM_LBUTTONUP 소스 수정
CRect *m_pLine;
if(m_bPaint==FALSE)
return;
m_bPaint=FALSE;
CClientDC dc(this);
dc.MoveTo (m_nowP);
dc.LineTo(point);
m_pLine = new CRect(m_nowP,point);
CDrawDoc* pDoc =GetDocument();
pDoc->m_arLine.AddTail((CObject *)m_pLine);
8. View 클래스의 OnDraw에 작성
CRect *R;
POSITION p=pDoc->m_arLine.GetHeadPosition();
while (p!=NULL)
{
R=(CRect *)pDoc->m_arLine.GetNext(p);
pDC->MoveTo(R->left,R->top);
pDC->LineTo(R->right,R->bottom);
}
9. 이전 데이터를 삭제하기 위하여 Doc 클래스의 DeleteContents()를 호출하여 코드 작성
while(!m_arLine.IsEmpty())
{
delete (CRect *)m_arLine.RemoveHead();
}
10. Doc 클래스의 Serialize 함수에 작성
CRect *R,Rect;
if (ar.IsStoring())
{
POSITION p=m_arLine.GetHeadPosition();
ar.WriteCount(m_arLine.GetCount());
while (p!=NULL)
{
R=(CRect *)m_arLine.GetNext(p);
Rect=*R;
ar << Rect;
}
}
else
{
DWORD nNewCount=ar.ReadCount();
while (nNewCount--)
{
ar >> Rect;
R=new CRect(Rect);
m_arLine.AddTail((CObject *)R);
}
}
11. View 클래스의 WM_LBUTTONUP에 추가
pDoc->SetModifiedFlag(TRUE);
4. CMetaFileDC
1)사용
CMetaFileDC는 GDI 명령어를 저장하기 위한 DC입니다.
이 DC는 사용법이 화면 DC와 유사합니다.
이 DC 객체는 생성만으로는 사용할 수 없으며 Create()를 호출해야만 사용할 수 있습니다.
이 때 Create()의 매개변수로 WMF나 EMF 파일의 확장자를 주면 열거나 저장할 수 있습니다.
이 DC를 이용해서 GDI 함수를 호출할 수 있으며 이를 저장하고자 할 때는 HMETAFILE 객체를생성해서 Close 함수를 호출해서 리턴시키면 됩니다.
HMETAFILE핸들은 GetMetaFile("파일경로 및 파일명")으로 생성할 수 있습니다.
이 DC를 이용해서 화면에 출력하고자 할 때는 화면 DC에서 PlayMetaFile(메타파일객체)을 호출하면 됩니다.
예제1)
- SDI 옵션으로 프로젝트 생성
- View 클래스에 HMETAFILE 객체 생성
HMETAFILE m_hMeta;
- View 클래스의 마우스 왼쪽 버튼 메시지의 클릭 메시지 작성
CClientDC dc(this);
CMetaFileDC mdc;
mdc.Create();
mdc.Rectangle(100,100,300,300);
mdc.Ellipse(100,100,300,300);
m_hMeta = mdc.Close();
dc.PlayMetaFile(m_hMeta);
위의 프로젝트에 메뉴 추가
메타파일
- 메타파일 저장(ID_METASAVE)
- 메타파일 열기(ID_METAOPEN)
1) ID_METASAVE의 COMMAND 메시지 작성
CClientDC dc(this);
CMetaFileDC mDC;
HMETAFILE hMetaFile;
mDC.Create(TEXT("c:\\Test.wmf"));
mDC.Rectangle(0, 0, 100, 100);
dc.Rectangle(0, 0, 100, 100);
mDC.TextOut(10, 10, TEXT("METAFILE"));
dc.TextOut(10, 10, TEXT("METAFILE"));
hMetaFile = mDC.Close();
::DeleteMetaFile(hMetaFile);
2) ID_METAOPEN의 COMMAND 메시지 작성
RedrawWindow();
CClientDC dc(this);
CMetaFileDC mDC;
HMETAFILE hMetaFile = ::GetMetaFile(TEXT("C:\\test.wmf"));
dc.PlayMetaFile(hMetaFile);
::DeleteMetaFile(hMetaFile);
'C & C++ > C & C++' 카테고리의 다른 글
[MFC] Sheel 프로그래밍 (0) | 2011.04.25 |
---|---|
[MFC] DLL( Dynamic Linking Library) (0) | 2011.04.25 |
[MFC] 대화 상자 (0) | 2011.04.25 |
[MFC] Message 처리 (0) | 2011.04.25 |
[MFC] 계층적 구조 (0) | 2011.04.25 |
댓글