스트림(TStream) 종류별 설명 및 예시
델파이에서 스트림(Stream)은 데이터의 연속적인 흐름을 추상화한 개념입니다. System.Classes
유닛에 정의된 TStream
은 모든 스트림 클래스의 추상 기반 클래스로, 데이터가 메모리에 있든, 파일에 있든, 네트워크를 통해 전송되든 상관없이 일관된 방식으로 데이터를 읽고 쓸 수 있는 인터페이스를 제공합니다.
TStream
자체는 추상 클래스이므로 직접 인스턴스화할 수 없으며, 실제 작업에는 이를 상속받아 구현된 구체적인 스트림 클래스들을 사용합니다. 주요 메서드로는 데이터를 읽는 Read
, 쓰는 Write
, 스트림 내 위치를 이동하는 Seek
가 있으며, Position
(현재 위치), Size
(전체 크기) 등의 속성을 공통으로 가집니다.
델파이 스트림 종류별 설명 및 예시
1. TMemoryStream
메모리 블록을 스트림으로 다룹니다. 동적으로 크기가 조절되는 메모리 버퍼에 데이터를 읽고 쓸 때 사용합니다. 비교적 작은 크기의 데이터를 임시로 저장하거나, 다른 스트림으로 데이터를 전송하기 전 중간 버퍼로 활용하기에 좋습니다.
- 주요 용도: 임시 데이터 저장, 이미지/데이터 변환 버퍼, 다른 컴포넌트 간 데이터 전달.
- 특징:
LoadFromFile
,SaveToFile
메서드를 통해 파일과 쉽게 데이터를 주고받을 수 있습니다.
TMemoryStream 예제
메모리 스트림에 정수와 문자열을 쓰고 다시 읽어오는 예제입니다.
uses System.Classes, System.SysUtils;
procedure MemoryStreamExample;
var
MemStream: TMemoryStream;
IntValue: Integer;
StrValue: string;
StrLen: Integer;
Buffer: TBytes;
begin
MemStream := TMemoryStream.Create;
try
// 정수 값 쓰기 (4바이트)
IntValue := 12345;
MemStream.Write(IntValue, SizeOf(Integer));
// 문자열 쓰기 (길이 정보 + 문자열 데이터)
StrValue := 'Hello Delphi Stream!';
StrLen := Length(StrValue); // 문자열 길이
MemStream.Write(StrLen, SizeOf(Integer)); // 길이 먼저 쓰기 (4바이트)
// 문자열 데이터 쓰기 (UTF8 인코딩된 바이트 사용 권장)
Buffer := TEncoding.UTF8.GetBytes(StrValue);
MemStream.Write(Buffer, Length(Buffer));
// 읽기 위해 위치를 처음으로 이동
MemStream.Position := 0;
// 정수 값 읽기
MemStream.Read(IntValue, SizeOf(Integer));
ShowMessage('읽은 정수: ' + IntToStr(IntValue)); // 결과: 12345
// 문자열 읽기 (길이 먼저 읽고, 그 길이만큼 데이터 읽기)
MemStream.Read(StrLen, SizeOf(Integer)); // 길이 읽기
SetLength(Buffer, StrLen); // 버퍼 크기 조정 (UTF8 바이트 길이 기준)
if StrLen > 0 then
MemStream.Read(Buffer, StrLen); // 데이터 읽기
StrValue := TEncoding.UTF8.GetString(Buffer); // UTF8 바이트를 문자열로 변환
ShowMessage('읽은 문자열: ' + StrValue); // 결과: Hello Delphi Stream!
// 파일로 저장 (선택적)
// MemStream.SaveToFile('C:\Temp\MemoryData.dat');
finally
MemStream.Free; // 반드시 해제
end;
end;
참고: 문자열을 쓸 때는 길이 정보를 함께 저장해야 나중에 정확히 읽어올 수 있습니다. 또한, 문자열 인코딩(예: UTF-8)을 명시적으로 처리하는 것이 안전합니다.
2. TFileStream
디스크 파일을 스트림으로 다룹니다. 파일의 내용을 읽거나 파일에 데이터를 쓸 때 사용합니다. 생성자에서 파일명과 파일 열기 모드(읽기 전용, 쓰기 전용, 생성 등)를 지정해야 합니다.
- 주요 용도: 파일 읽기/쓰기, 파일 복사/이동, 대용량 데이터 처리.
- 주의사항: 파일 작업 완료 후 반드시
Free
를 호출하여 파일 핸들을 해제해야 합니다.try...finally
구문 사용이 필수적입니다.
TFileStream 예제
파일 스트림을 사용하여 텍스트 파일에 내용을 쓰고 읽는 예제입니다.
uses System.Classes, System.SysUtils, System.IOUtils;
procedure FileStreamExample;
var
FileStream: TFileStream;
FilePath: string;
TextToWrite: string;
ReadBuffer: TBytes;
ReadString: string;
begin
FilePath := TPath.Combine(TPath.GetTempPath, 'MyFileStreamTest.txt'); // 임시 폴더에 파일 생성
// 파일에 쓰기 (파일이 없으면 생성, 있으면 덮어씀)
FileStream := TFileStream.Create(FilePath, fmCreate);
try
TextToWrite := '델파이 파일 스트림 테스트입니다.' + sLineBreak + '두 번째 줄입니다.';
ReadBuffer := TEncoding.UTF8.GetBytes(TextToWrite); // UTF-8 인코딩
FileStream.Write(ReadBuffer, Length(ReadBuffer));
ShowMessage(FilePath + ' 파일 쓰기 완료');
finally
FileStream.Free; // 쓰기 완료 후 해제
end;
// 파일에서 읽기 (읽기 모드로 열기)
if TFile.Exists(FilePath) then
begin
FileStream := TFileStream.Create(FilePath, fmOpenRead or fmShareDenyNone); // 읽기 + 공유 허용
try
SetLength(ReadBuffer, FileStream.Size); // 파일 크기만큼 버퍼 설정
FileStream.Read(ReadBuffer, FileStream.Size); // 전체 내용 읽기
ReadString := TEncoding.UTF8.GetString(ReadBuffer); // UTF-8 디코딩
ShowMessage(FilePath + ' 파일 내용:' + sLineBreak + ReadString);
finally
FileStream.Free; // 읽기 완료 후 해제
end;
// TFile.Delete(FilePath); // 테스트 후 파일 삭제 (선택적)
end
else
begin
ShowMessage(FilePath + ' 파일을 찾을 수 없습니다.');
end;
end;
3. TStringStream
문자열(String) 데이터를 스트림처럼 다룹니다. 내부적으로 문자열 데이터를 메모리에 유지하며, 이를 읽거나 쓸 수 있습니다. 특히 텍스트 인코딩(ANSI, UTF-8 등)을 처리할 때 유용합니다.
- 주요 용도: 문자열 기반 데이터 처리, 텍스트 인코딩 변환, 메모리 상에서의 문자열 조작.
- 주요 속성:
DataString
속성을 통해 내부 문자열 데이터에 직접 접근할 수 있습니다. 생성자에서 초기 문자열과 인코딩을 지정할 수 있습니다.
TStringStream 예제
문자열 스트림을 사용하여 메모리 상에서 텍스트 데이터를 조작하는 예제입니다.
uses System.Classes, System.SysUtils;
procedure StringStreamExample;
var
StringStream: TStringStream;
InitialString: string;
WriteString: string;
ReadString: string;
ReadBuffer: array[0..255] of Byte; // 작은 버퍼
BytesRead: Integer;
begin
InitialString := '초기 문자열 데이터. ';
// UTF-8 인코딩을 명시하여 StringStream 생성
StringStream := TStringStream.Create(InitialString, TEncoding.UTF8);
try
// 스트림 끝에 문자열 추가 (Write 사용 시 바이트 배열 필요)
WriteString := '추가된 문자열입니다.';
// Position을 끝으로 이동해야 추가됨
StringStream.Position := StringStream.Size;
BytesRead := StringStream.Write(TEncoding.UTF8.GetBytes(WriteString), Length(TEncoding.UTF8.GetBytes(WriteString)));
ShowMessage(IntToStr(BytesRead) + ' 바이트 추가됨.');
// DataString 속성으로 전체 내용 확인
ShowMessage('전체 내용 (DataString): ' + StringStream.DataString);
// 처음부터 일부 내용 읽기 (Read 사용)
StringStream.Position := 0; // 위치를 처음으로
BytesRead := StringStream.Read(ReadBuffer, 10); // 최대 10바이트 읽기
ReadString := TEncoding.UTF8.GetString(ReadBuffer, 0, BytesRead); // 읽은 만큼만 변환
ShowMessage('처음 10바이트 읽기 (Read): ' + ReadString);
finally
StringStream.Free;
end;
end;
4. TResourceStream
애플리케이션의 리소스(Resource) 데이터를 스트림으로 다룹니다. 실행 파일(.exe)이나 DLL에 포함된 이미지, 아이콘, 문자열 테이블 등의 리소스 데이터를 읽어올 때 사용합니다.
- 주요 용도: 프로그램에 내장된 리소스(이미지, 사운드, 텍스트 등) 접근.
- 생성 방법: 리소스 핸들과 리소스 이름(또는 ID)을 이용하여 생성합니다.
TResourceStream 예제
애플리케이션 리소스에 포함된 'MY_TEXT'라는 이름의 RCDATA를 읽어오는 예제입니다.
주의: 이 예제를 실행하려면 프로젝트에 실제 'MY_TEXT'라는 이름의 RCDATA 리소스가 포함되어 있어야 합니다. (예: .rc 파일을 통해 추가하고, {$R *.res}
지시문 사용)
uses System.Classes, System.SysUtils;
// 프로젝트 파일(.dpr)이나 유닛에 리소스 파일 링크 필요
// {$R MyResources.res} // 가정: MyResources.res 파일에 MY_TEXT 리소스가 있음
procedure ResourceStreamExample;
var
ResStream: TResourceStream;
ResBytes: TBytes;
ResString: string;
ResourceName: string;
begin
ResourceName := 'MY_TEXT'; // 리소스 이름 (대소문자 구분 주의)
try
// HInstance는 현재 모듈(EXE 또는 DLL) 핸들, 리소스 이름, 리소스 타입(RT_RCDATA) 지정
ResStream := TResourceStream.Create(HInstance, ResourceName, RT_RCDATA);
except
on E: EResNotFound do
begin
ShowMessage('리소스 ''' + ResourceName + '''를 찾을 수 없습니다.');
Exit; // 리소스 없으면 종료
end;
else
raise; // 다른 예외는 다시 발생시킴
end;
try
SetLength(ResBytes, ResStream.Size); // 리소스 크기만큼 버퍼 할당
ResStream.Read(ResBytes, ResStream.Size); // 리소스 데이터 읽기
// 리소스 데이터가 텍스트라고 가정하고 UTF-8로 디코딩
ResString := TEncoding.UTF8.GetString(ResBytes);
ShowMessage('리소스 ''' + ResourceName + ''' 내용:' + sLineBreak + ResString);
finally
ResStream.Free; // 반드시 해제
end;
end;
5. TBlobStream
데이터베이스의 BLOB(Binary Large Object) 필드를 스트림으로 다룹니다. TDataSet
컴포넌트의 BLOB 필드(이미지, 동영상, 문서 파일 등 대용량 바이너리 데이터)를 읽거나 쓸 때 사용됩니다.
- 주요 용도: 데이터베이스 BLOB 필드 데이터 처리.
- 생성 방법:
TDataSet.CreateBlobStream
메서드를 통해 특정 필드와 연결된 스트림 객체를 생성합니다.
TBlobStream 예제 (개념 설명)
TBlobStream
은 데이터베이스 필드와 직접 연동되므로, 간단한 독립 실행 예제보다는 데이터셋 컴포넌트(예: TFDQuery
, TADOQuery
, TClientDataSet
등)와 함께 사용됩니다.
일반적인 사용 패턴은 다음과 같습니다:
- 데이터셋 컴포넌트에서 BLOB 필드를 가져옵니다 (예:
MyQuery.FieldByName('ImageData') as TBlobField
). - 해당 필드에 대해
CreateBlobStream
메서드를 호출하여TBlobStream
객체를 생성합니다. (읽기 또는 쓰기 모드 지정) - 생성된 스트림 객체를 사용하여 데이터를 읽거나 씁니다 (예:
TMemoryStream
이나TFileStream
으로 로드/저장). - 작업 완료 후 스트림 객체를
Free
합니다.
uses System.Classes, Data.DB; // DB 관련 유닛 필요
// 가정: MyQuery 라는 이름의 TDataSet 후손 컴포넌트가 있고,
// 'BlobData' 라는 이름의 BLOB 필드가 존재하며,
// MyQuery가 활성 상태이고 레코드를 가리키고 있음.
procedure BlobStreamReadExample(Query: TDataSet; FieldName: string; TargetStream: TStream);
var
BlobField: TBlobField;
BlobStream: TStream; // TBlobStream은 TStream의 후손
begin
BlobField := Query.FieldByName(FieldName) as TBlobField; // 필드 가져오기
if BlobField = nil then Exit;
// 읽기 모드로 BLOB 스트림 생성
BlobStream := Query.CreateBlobStream(BlobField, bmRead);
try
// 대상 스트림(예: TMemoryStream)으로 BLOB 데이터 복사
TargetStream.CopyFrom(BlobStream, BlobStream.Size);
ShowMessage(FieldName + ' 필드 데이터를 읽었습니다. 크기: ' + IntToStr(TargetStream.Size));
finally
BlobStream.Free; // BLOB 스트림 해제
end;
end;
procedure BlobStreamWriteExample(Query: TDataSet; FieldName: string; SourceStream: TStream);
var
BlobField: TBlobField;
BlobStream: TStream;
begin
BlobField := Query.FieldByName(FieldName) as TBlobField;
if BlobField = nil then Exit;
// 쓰기 모드로 BLOB 스트림 생성 (먼저 Edit 상태여야 함)
if not (Query.State in [dsEdit, dsInsert]) then
Query.Edit; // 편집 모드로 전환
BlobStream := Query.CreateBlobStream(BlobField, bmWrite);
try
// 원본 스트림(예: TMemoryStream) 내용을 BLOB 필드로 복사
SourceStream.Position := 0; // 소스 스트림 위치 초기화
BlobStream.CopyFrom(SourceStream, SourceStream.Size);
ShowMessage(FieldName + ' 필드에 데이터를 썼습니다. 크기: ' + IntToStr(BlobStream.Size));
// Query.Post; // 변경사항 저장 필요
finally
BlobStream.Free;
end;
end;
// 사용 예시:
// var ms: TMemoryStream;
// begin
// ms := TMemoryStream.Create;
// try
// // BLOB 필드 -> 메모리 스트림
// BlobStreamReadExample(MyQuery, 'BlobData', ms);
// // ... ms 내용을 사용 ...
//
// // 메모리 스트림 -> BLOB 필드 (다른 레코드나 필드에 쓸 경우)
// ms.Position := 0; // 쓸 때는 위치를 처음으로
// BlobStreamWriteExample(MyOtherQuery, 'TargetBlobField', ms);
// finally
// ms.Free;
// end;
// end;
참고: 실제 데이터베이스 연동 코드는 사용하는 데이터 접근 컴포넌트(FireDAC, ADO 등)와 데이터베이스 종류에 따라 세부적인 구현이 달라질 수 있습니다.
결론
델파이의 스트림 아키텍처는 다양한 데이터 소스를 일관된 방식으로 처리할 수 있게 해주는 강력한 기능입니다. TMemoryStream
, TFileStream
, TStringStream
등 목적에 맞는 스트림 클래스를 선택하여 사용하면 파일 I/O, 메모리 데이터 조작, 리소스 접근, 데이터베이스 BLOB 처리 등 복잡한 작업을 보다 쉽고 효율적으로 구현할 수 있습니다.
'코딩 이야기 > 델파이 코딩' 카테고리의 다른 글
델파이 Class 설명 및 예제 (0) | 2025.04.28 |
---|---|
델파이 배열(Array) 종류별 설명 및 예시 (0) | 2025.04.28 |
델파이 TStringList 설명 및 예시 (0) | 2025.04.28 |
델파이 함수 및 프로시저 만들기 (0) | 2025.04.23 |
델파이 Private, Public 접근 지정자 설명 및 예시 (0) | 2025.04.22 |