Main Content

파일 입력 및 출력을 위한 새로운 System object 만들기

이 예제에서는 MATLAB®의 데이터 입력 및 출력 스트리밍을 용이하게 하기 위해 두 가지 다른 System object인 TextFileReaderTextFileWriter를 만들고 사용하는 방법을 보여줍니다.

이 예제에서 설명하는 객체는 여러 가지 현실적인 사용 사례를 해결하며, 더욱 수준 높고 전문적인 작업을 수행할 수 있도록 사용자 지정이 가능합니다.

소개

System object는 matlab.System에서 파생되는 MATLAB 클래스입니다. 결과적으로, 모든 System object는 다음과 같은 표준 메서드를 포함하는 공통된 퍼블릭 인터페이스를 상속합니다.

  • setup — 일반적으로 시뮬레이션을 시작할 때 객체를 초기화함

  • reset — 객체의 내부 상태를 지워 디폴트 초기화-후 상태로 되돌림

  • release — 객체에서 내부적으로 사용되는 모든 리소스(메모리, 하드웨어 또는 OS별 리소스)를 해제함

새로운 종류의 System object를 만들 때는 동작을 결정하기 위해 위에 있는 모든 메서드에 대한 특정한 구현을 제공해야 합니다.

이 예제에서는 다음 두 가지 System object의 내부 구조와 사용법을 설명합니다.

  • TextFileReader

  • TextFileWriter

MATLAB의 데이터 입력 및 출력 스트리밍을 위한 이러한 System object를 만들기 위해 이 예제에서는 MATLAB에서 사용 가능한 표준 로우 레벨 파일 I/O 함수(예: fscanf, fread, fprintf, fwrite)를 사용합니다. 이들 객체는 이러한 함수에 대한 대부분의 사용 세부 정보를 추상화함으로써 스트리밍된 데이터를 읽고 쓰는 작업을 더 간단하고 효율적으로 만드는 것을 목표로 합니다.

이 예제에서는 System object를 작성하기 위한 여러 가지 고급 구문이 사용됩니다. System object 작성에 대한 더 기초적인 소개는 System object 생성하기 항목을 참조하십시오.

TextFileReader 클래스의 정의

TextFileReader 클래스는 클래스 정의, 퍼블릭 속성과 프라이빗 속성, 생성자, matlab.System 기본 클래스에서 재정의된 보호 메서드, 프라이빗 메서드를 포함합니다. TextFileWriter 클래스의 구조도 이와 유사합니다.

클래스 정의

클래스 정의는 TextFileReader 클래스가 matlab.Systemmatlab.system.mixin.FiniteSource 양쪽에서 파생된다는 점을 명시합니다.

   classdef (StrictDefaults)TextFileReader < matlab.System & matlab.system.mixin.FiniteSource
  • matlab.System은 필수적이며 모든 System object에 대한 기본 클래스입니다.

  • matlab.system.mixin.FiniteSource는 이 클래스가 유한한 개수의 데이터 샘플을 가진 신호 소스임을 나타냅니다. 이 클래스 유형의 경우, System object™는 일반적인 인터페이스 외에 isDone 함수도 노출합니다. isDone이 true를 반환하면 객체가 사용 가능한 데이터의 끝에 도달한 것입니다.

퍼블릭 속성

퍼블릭 속성은 사용자가 객체의 동작을 자신의 특정 응용 사례에 맞게 조정할 목적으로 변경할 수 있습니다. TextFileReader에는 조정 불가형 퍼블릭 속성 2개(객체에 대한 첫 번째 호출 전에만 변경 가능)와 조정 가능형 퍼블릭 속성 4개가 있습니다. 모든 퍼블릭 속성에는 디폴트 값이 있습니다. 사용자가 값을 지정하지 않을 경우 디폴트 값이 이에 대응되는 속성에 할당됩니다.

   properties (Nontunable)
       Filename   = 'tempfile.txt' 
       HeaderLines = 4
   end
   properties
       DataFormat = '%g' 
       Delimiter = ',' 
       SamplesPerFrame = 1024
       PlayCount = 1
   end

프라이빗 속성

프라이빗 속성은 사용자에게 표시되지 않으며 다음을 포함한 여러 가지 목적으로 사용할 수 있습니다.

  • 가끔씩만 계산되며 계산된 후에는 알고리즘에 대한 후속 호출에 사용되는 값 유지. setup이 호출되거나 객체가 처음으로 호출될 때 발생하는 초기화 시 사용되는 값을 예로 들 수 있습니다. 이를 통해 런타임에 값을 다시 계산할 필요가 없게 되므로 핵심 기능의 성능을 개선할 수 있습니다.

  • 객체의 내부 상태 정의. 예를 들어, pNumEofReached는 파일 끝(EOF) 표시자에 도달한 횟수를 저장합니다.

   properties(Access = private)
       pFID = -1 
       pNumChannels 
       pLineFormat 
       pNumEofReached = 0
   end

생성자

생성자는 이름-값 쌍을 사용하여 TextFileReader 객체를 생성할 수 있도록 정의됩니다. 이 생성자는 TextDataReader의 새 인스턴스가 생성될 때 호출됩니다. 생성자 내 setProperties에 대한 호출은 생성 시 이름-값 쌍으로 속성을 설정할 수 있도록 허용합니다. 생성자에는 다른 어떠한 초기화 작업도 지정하면 안 됩니다. 대신, setupImpl 메서드를 사용하십시오.

   methods
       function obj = TextFileReader(varargin)
           setProperties(obj, nargin, varargin{:});
       end
   end

matlab.System 기본 클래스 보호 메서드 재정의하기

모든 System object에 공통된 퍼블릭 메서드는 각각 내부적으로 호출하는 해당 보호 메서드가 있습니다. 이런 보호 메서드의 이름은 모두 Impl 접미사를 포함합니다. System object의 동작을 프로그래밍하기 위한 클래스를 정의할 때 이러한 메서드를 구현할 수 있습니다.

표준 퍼블릭 메서드와 그 내부 구현 사이의 대응 관계에 관한 자세한 내용은 호출 순서 요약 항목을 참조하십시오.

예를 들어, TextFileReader는 다음 Impl 메서드를 재정의합니다.

  • setupImpl

  • resetImpl

  • stepImpl

  • releaseImpl

  • isDoneImpl

  • processTunedPropertiesImpl

  • loadObjectImpl

  • saveObjectImpl

프라이빗 메서드

프라이빗 메서드는 같은 클래스의 다른 메서드 내에서만 액세스 가능합니다. 이러한 메서드를 사용하여 코드의 나머지 부분을 더 읽기 쉽게 만들 수 있습니다. 또한 클래스의 다른 부분에서 여러 번 사용되는 코드를 별개의 루틴 코드로 그룹화하여 코드 재사용 가능성도 개선할 수 있습니다. TextFileReader의 경우 다음에 대해 프라이빗 메서드가 생성됩니다.

  • getWorkingFID

  • goToStartOfData

  • peekCurrentLine

  • lockNumberOfChannelsUsingCurrentLine

  • readNDataRows

데이터 쓰기와 읽기

이 예제에서는 다음과 같이 TextFileReaderTextFileWriter를 사용할 수 있는 방법을 보여줍니다.

  • TextFileWriter를 사용하여 두 가지 다른 정현파 신호의 샘플을 포함한 텍스트 파일 생성

  • TextFileReader를 사용하여 텍스트 파일에서 읽기.

간단한 텍스트 파일 만들기

각각 50Hz 및 60Hz의 주파수를 갖는 두 개의 정현파 신호를 저장하는 새로운 파일을 만듭니다. 각 신호에 대해 저장된 데이터는 8kHz의 샘플링 레이트에서 800개의 샘플로 구성됩니다.

데이터 샘플을 만듭니다.

fs = 8000;
tmax = 0.1;
t = (0:1/fs:tmax-1/fs)';
N = length(t);
f = [50,60];
data = sin(2*pi*t*f);

향후에 이용하기 위해, 읽을 수 있는 방식으로 데이터를 설명하는 헤더 문자열을 형성합니다(선택적 단계).

fileheader = sprintf(['The following contains %d samples of two ',...
    'sinusoids,\nwith frequencies %d Hz and %d Hz and a sample rate of',...
    ' %d kHz\n\n'], N, f(1),f(2),fs/1000);

신호를 텍스트 파일에 저장하기 위해 TextFileWriter 객체를 만듭니다. TextFileWriter의 생성자에는 대상 파일 이름과 이름-값 쌍으로 전달될 수 있는 몇 가지 선택적 파라미터가 필요합니다.

TxtWriter = TextFileWriter('Filename','sinewaves.txt','Header',fileheader)
TxtWriter = 
  TextFileWriter with properties:

      Filename: 'sinewaves.txt'
        Header: 'The following contains 800 samples of two sinusoids,...'
    DataFormat: '%.18g'
     Delimiter: ','

TextFileWriter는 구분 기호로 구분된 ASCII 파일에 데이터를 씁니다. 이 객체의 퍼블릭 속성은 다음과 같습니다.

  • Filename - 작성할 파일의 이름. 이 이름을 가진 파일이 이미 존재하는 경우에는 이 파일을 덮어씁니다. 작업 시작 시, 이 객체는 파일의 헤더 바로 다음부터 데이터를 씁니다. 그런 다음, 이 객체는 해제되기 전까지 이 객체에 대한 후속 호출 시마다 새 데이터를 추가합니다. reset을 호출하면 파일의 시작 부분부터 쓰기가 다시 시작됩니다.

  • Header - 종종 여러 줄로 구성되고 새 줄 문자(\n)로 종료되는 문자열. 이 속성은 사용자가 지정하며, 사람이 읽을 수 있는 형식으로 실제 데이터를 설명하는 정보를 포함하도록 수정될 수 있습니다.

  • DataFormat — 각 데이터 샘플을 저장하는 데 사용되는 형식. 이 속성은 내장된 MATLAB 함수 fprintf에서 사용하는 formatSpec 문자열 내에서 변환 지정자로 할당 가능한 모든 값을 취할 수 있습니다. DataFormat은 파일에 기록된 모든 채널에 적용됩니다. 이 속성에 대한 디폴트 값은 '%.18g'이며, 이 값은 배정밀도 부동소수점 데이터를 최대 정밀도로 저장할 수 있도록 허용합니다.

  • Delimiter — 같은 시점에서 서로 다른 채널의 샘플을 구분하는 데 사용되는 문자. 작성된 파일의 모든 라인이 각각 한 시점에 매핑되고, 이 라인은 입력값으로 제공된 채널 개수(즉, 객체로 전달된 행렬 입력값의 열 개수)와 같은 수의 샘플을 포함합니다.

한 번의 호출로 사용 가능한 모든 데이터를 파일에 쓸 수 있습니다.

TxtWriter(data)

release 함수를 호출하여 파일에 대한 제어를 해제합니다.

release(TxtWriter)

데이터가 이제 새 파일에 저장되었습니다. 파일을 직접 눈으로 보며 검사하려면 다음을 입력하십시오.

edit('sinewaves.txt')

헤더가 세 개의 라인을 차지하므로 데이터는 라인 4에서 시작됩니다.

이 간단한 사례에서는 전체 신호의 길이가 작으며 시스템 메모리에 잘 맞습니다. 따라서 한 단계로 데이터를 한꺼번에 만들어 파일에 쓸 수 있습니다.

이 접근 방식이 불가능하거나 실용적이지 않은 경우가 있습니다. 예를 들어, 데이터가 너무 커서 단일 MATLAB 변수에는 맞지 않을 수 있습니다(너무 커서 시스템 메모리에 맞지 않음). 또는 데이터가 루프에서 순환 방식으로 생성되거나 외부 소스에서 MATLAB으로 스트리밍될 수 있습니다. 이 모든 경우에 다음 예제와 유사한 접근 방식으로 파일로 데이터를 스트리밍할 수 있습니다.

스트리밍된 사인파 생성기를 사용하여 루프마다 데이터 프레임을 만듭니다. 원하는 횟수만큼 반복 실행하여 데이터를 만들고 파일에 저장합니다.

frameLength = 32;
tmax = 10; 
t = (0:1/fs:tmax-1/fs)';
N = length(t);
data = sin(2*pi*t*f);
numCycles = N/frameLength;

for k = 1:10 % Long running loop when you replace 10 with numCycles. 
    dataFrame = sin(2*pi*t*f);
    TxtWriter(dataFrame)
end

release(TxtWriter)

기존 텍스트 파일에서 읽기

텍스트 파일에서 읽으려면 TextFileReader의 인스턴스를 만드십시오.

TxtReader = TextFileReader('Filename','sinewaves.txt','HeaderLines',3,'SamplesPerFrame',frameLength)
TxtReader = 
  TextFileReader with properties:

           Filename: 'sinewaves.txt'
        HeaderLines: 3
         DataFormat: '%g'
          Delimiter: ','
    SamplesPerFrame: 32
          PlayCount: 1

TextFileReader는 구분 기호로 구분된 ASCII 파일에서 숫자형 데이터를 읽습니다. 이 객체의 속성은 TextFileWriter의 속성과 유사합니다. 몇 가지 차이점은 다음과 같습니다.

  • HeaderLinesFilename에 지정된 파일 내 헤더에 사용되는 라인의 수. 객체에 대한 첫 번째 호출은 라인 번호 HeaderLines+1에서 읽기 작업을 시작합니다. 객체에 대한 후속 호출은 이전에 읽은 라인 바로 다음 라인부터 계속 읽기 작업을 수행합니다. reset을 호출하면 라인 HeaderLines+1에서 읽기 작업이 다시 시작됩니다.

  • Delimiter — 같은 시점에서 서로 다른 채널의 샘플을 구분하는 데 사용되는 문자. 이 경우, 구분 기호는 파일에 저장된 데이터 채널의 개수를 확인하는 데도 사용됩니다. 객체는 처음 실행될 때 라인 HeaderLines+1에서 Delimiter 문자의 개수를 셉니다(예: numDel). 그런 다음, 모든 시점에 대해 객체는 DataFormat 형식의 numChan = numDel+1 숫자형 값을 읽습니다. 알고리즘에서 반환되는 행렬의 크기는 SamplesPerFrame×numChan입니다.

  • SamplesPerFrame — 객체에 대한 각 호출에서 읽은 라인의 개수. 또한 이 값은 출력값으로 반환되는 행렬의 행 개수이기도 합니다. 마지막으로 사용 가능한 데이터 행에 도달했을 때 그 수가 필요한 SamplesPerFrame보다 적을 수 있습니다. 그럴 경우, 사용 가능한 데이터를 0으로 채워 크기가 SamplesPerFrame×numChan인 행렬을 얻습니다. 모든 데이터를 읽은 후, 이 알고리즘은 reset 또는 release가 호출될 때까지 zeros(SamplesPerFrame,numChan)만 반환합니다.

  • PlayCount — 파일의 데이터를 순환적으로 읽는 횟수. 이 객체가 파일의 끝에 도달하고 PlayCount와 같은 횟수만큼 파일을 아직 읽지 않은 경우 데이터의 시작 부분(라인 HeaderLines+1)부터 읽기가 다시 시작됩니다. 파일의 마지막 라인에 SamplesPerFrame×numChan 크기의 완전한 출력 행렬을 형성할 만큼 충분한 샘플이 없는 경우에는 초기 데이터를 사용하여 프레임이 완성됩니다. 파일을 PlayCount회 읽고 나면 알고리즘에서 반환되는 출력 행렬이 0으로 채워지고 reset 또는 release가 호출되지 않는 한 isDone에 대한 모든 호출이 true를 반환합니다. 사용 가능한 데이터를 무한정 순환하려면 PlayCountInf로 설정하면 됩니다.

텍스트 파일에서 데이터를 읽을 때는 더 일반적인 스트리밍 접근 방식이 사용됩니다. 이 데이터 읽기 방법은 매우 큰 데이터를 처리하는 경우와도 관련이 있습니다. frameLength개의 행과 2개의 열을 가진 데이터 프레임을 사전할당합니다.

dataFrame = zeros(frameLength,2,'single');

데이터가 소스 텍스트 파일에 있는 동안 텍스트 파일에서 데이터를 읽어 이진 파일에 씁니다. 메서드 isDone을 사용하여 while 루프의 실행을 제어하는 방법을 확인하십시오.

while(~isDone(TxtReader))
    dataFrame(:) = TxtReader();
end

release(TxtReader)

요약

이 예제에서는 숫자형 데이터 파일에서 데이터를 읽고 쓰기 위한 System object를 작성하고 사용하는 방법을 보여주었습니다. 특수한 목적의 파일 읽기 및 쓰기 작업을 수행하도록 TextFileReaderTextFileWriter를 편집할 수 있습니다. 또한 이러한 사용자 지정 System object를 내장 System object(예: dsp.BinaryFileWriterdsp.BinaryFileReader)와 결합할 수 있습니다.

사용자 지정 알고리즘을 위한 System object 작성에 관한 자세한 내용은 System object 생성하기 항목을 참조하십시오.

관련 항목