Devlog
article thumbnail
Published 2022. 1. 13. 18:12
[2021-2] 운영체제 프로젝트 학교

과제 제목 : 윈도우 운영체제에서 파일의 내용을 버퍼로 읽어들이고, 버퍼로 읽어들인 파일의 내용을 20%이상 수정한후, 새로운 파일로 저장하는 기능에 대해서 프로그램한다.

O 개발에 사용할 프로그램 언어 : C프로그램(C++, C# 포함), Java, Python 언어중 하나로 작성
O 팀 구성 방법 : 개인별로 term project를 수행합니다.

O 제출일시 : 2021.11.12.(금), 23:00까지
O 과제 제출 방법 : 과제수행 후 최종보고서 파일(파일이름:“학번_홍길동.hwp”)과 최종 개발 소스코드(파일이름:“학번_홍길동.zip”)를 제출해야 합니다. 제출은 DOOR시스템(교수 학습지원 시스템) 운영체제 교과목 과제제출 게시판으로 제출함.
O 리포트 작성 형식은 아래와 같습니다.

cover paer(과제제목, 제출일시, 참여연구원) <--- 1페이지
목차 <--- 1페이지
1장 서론 <--- 1페이지
2장 관련 연구 <--- 1페이지 이상
3장 과제 수행 내용 <--- 5페이지 이상
---> 수행 내용 마지막 부분에 개발일정 1 페이지 설명
---> 프로그램 코드 동작에 대한 자세한 설명
4장 실험 및 결과 <--- 3페이지 이상
---> 실험하면서 초기에 오류나는 기능과 관련한 내용들 추가해도 됨.
5장 결론 <--- 1페이지 이상
참고문헌 <---- 1페이지 이상


폰트는 바탕체, 11pt, 줄간격 16으로 해야함. 위 조건을 만족하지 않을 시 감점이 있습니다. 본 과제의 기본 점수는 10점임. 우수과제의 경우는 가점이 있습니다.


과제 수행 내용 :
O 윈도우 운영체제에서 fread()함수를 이용하여 파일의 데이터를 버퍼로 읽기한 후, 버퍼에 읽은 내용을 일부 수정(20%이상 수정)합니다. 그리고, 버퍼에 수정된 내용을 fwrite() 함수를 이용하여 다른 파일로 쓰기한다. fread(), fwrite() 함수 기능에 대해서 자료 조사한 내용을 3장 과제 수행 내용에 자세하게 정리하기 바랍니다.

O 리포트는 자세하게 작성하기 바랍니다.
O 실험에 사용할 파일 크기는 30MB이상 파일을 읽어들일 것.
O 버퍼로 읽어들이는 데이터 크기(즉, 버퍼 크기)를 2KB로 고정시키고, fread(), fwrite() 함수를 수행(fread()함수는 아래 참조)하도록 할 것.


size_t fread(
void *buffer,
size_t size,
size_t count,
FILE *stream
);

<매개 변수>
buffer : 데이터의 스토리지 위치
size : 항목 크기(바이트)
count : 읽힐 항목의 최대 수
stream : FILE 구조체에 대한 포인터

 


나는 C언어를 이용하여 과제를 수행함 

과제 채점 점수는 따로 기재가 안되어 있는데... 시험을 잘 봐서 인지 아니면 과제를 잘해서 인지 (사실 이건 아닌 것 같음)

아무튼 학점은 잘 받은 과목이라 볼 사람은 없겠지만 기록차 과제를 작성하고자 함 (레포트의 3, 4장만 기술함)

 

과제 수행 내용 (조건)에 따라 파일 크기는 30MB 이상 파일을 읽어야 하므로 1로 가득 채운 30MB이상의 hello.txt 파일 생성하였고

파일의 20%이상 수정하기 위해 파일의 80%, 20%를 판별하는 변수의 조건을 따로 설정함

 


 

#include <stdio.h> // 표준 입출력 기능 헤더 파일 
#include <stdlib.h> // 메모리 할당/해제 기능 헤더 파일

 

 #include <stdio.h>는 표준 입력 출력을 나타내며 입출력 기능과 관련된 정보를 담고 있다. 대표적으로 printf(), scanf(), getc(), putc(), fopen(), fclose(), remove(), fflush() 등을 포함하고 있다.

 

 #include <stdlib.h>는 표준 라이브러리를 나타내며 메모리 할당/해제 기능에 대한 정보를 내포하고 있다. 대표적으로 malloc(), free(), abort(), exit(), atol(), atoll(), atof(), rand() 등을 포함하고 있다.

 

 

 

int main(){
	FILE *inFile; // 파일 스트림 생성을 위한 FILE 포인터
	FILE *outFile; // 파일 스트림 생성을 위한 FILE 포인터

	int itemsRead; // 읽은 데이터 수를 저장하기 위한 변수
	int count = 2048; // 읽힐 항목의 최대 수
	int data[2048]; // 데이터의 스토리지 위치

	char *s1 = "Hello, world!\n"; // 파일 데이터를 수정할 내용

	int size = 0; // 파일의 크기를 저장할 변수
	int tuning = 0; // 파일의 80% 값을 저장할 변수

파일 스트림 생성을 위한 FILE 포인터를 inFile과 outFile로 나누어서 선언을 해주었다.

읽은 데이터 수를 저장하기 위한 변수 itemsRead를 선언하고 앞서 서론의 시사점 및 제한점에서 언급한 고정된 버퍼 크기 2KB를 고려하여 파일로부터 입력 받은 데이터를 저장하는 버퍼를 가리키는 포인터를 의미하는 data배열의 크기와 입력 횟수를 의미하는 count의 크기를 각각 2048로 지정해주었다.

character 타입의 s1 변수는 파일 데이터를 수정할 내용인 Hello, world\n로 선언해주었다. 또한 버퍼의 위치와 파일 내용을 20%이상 수정하기 위해 사용할 변수 size, tuning를 각각 선언해주었다.

 

 

 

	// hello.txt 파일을 읽기 모드(r)로 열기
	inFile = fopen("hello.txt", "r"); 

	if (!inFile){ // 파일이 NULL 값이라면
		// 오류메세지 출력
		fprintf(stderr, "File open error. Exiting program\n");
		exit(1); // 프로그램 종료
	}

fopen()는 stdio.h 헤더파일에 존재하며 파일 스트림을 생성하고 파일을 열어주는 함수이다.

fopen() 함수 원형은 FILE* fopen(const char* filename, const char* mode);로 첫 번째 인자인 filename는 파일의 경로와 이름을 동시에 표현한다. 두 번째 인자 mode는 파일의 접근 모드와 파일 입출력 모드를 의미하며 파일의 접근 모드 종류로는 아래 표와 같다.

 

 

표 1 파일의 접근 모드 
모드 설명
r 읽기 전용으로 파일을 연다.
파일이 없거나 찾을 수 없는 경우 호출에 실패한다.
w 쓰기 전용으로 파일을 연다.
지정한 파일 명이 있는 경우, 파일 내용을 모두 지우고 새로 생성한다.
지정한 파일 명이 없는 경우, 새로운 파일을 생성한다.
a 추가 쓰기 전용으로 파일을 연다.
지정한 파일이 있으면 파일의 끝에서 부터 내용을 추가한다.
r+ 파일을 읽고 쓰기 위해 파일을 연다.
지정한 파일이 있는 경우, 기존의 내용을 덮어쓴다.
지정한 파일이 없는 경우, 새로운 파일을 생성해서 데이터를 쓴다.
w+ 파일을 읽고 쓰기 위해 파일을 연다.
지정한 파일이 있는 경우, 파일의 내용을 모두 지우고 새 파일을 생성한다.
지정한 파일이 없는 경우, 새로운 파일을 생성한다.
a+ 파일을 읽고 추가로 쓰기 위해 연다.
지정한 파일이 있으면 파일의 끝에서 부터 내용을 추가한다.
나머지 기능은 r+ 모드와 같다.

 

필자는 원본 파일인 hello.txt파일을 r(읽기)모드로 열도록 하였으며 fopen()는 호출 실패할 경우 NULL 값을 반환한다는 점을 이용하여

if문을 통해 inFile 값이 NULL이라면 경고 메세지 File open error. Exiting program\n을 출력하고 프로그램을 종료하도록 구현하였다.

 

여기서 fprintf()란 File print의 약자로 printf의 사용에 file의 개념만 추가된 것이다. 흔하게 사용하는 printf()를 사용하지 않고 fprintf()를 사용하는 이유는 printf()는 stdout를 사용하여 버퍼링이 존재하는 반면에 stderr로 출력되는 fprintf() 메시지는 버퍼링 없이 즉시 출력하므로 문제가 생겼을 경우 즉시 출력할 수 있어 printf()를 사용하지 않고 fprintf()를 사용하였다.

 

 

	fseek(inFile, 0, SEEK_END); // 파일 포인터를 파일의 끝으로 이동
	size = ftell(inFile); // 파일 포인터의 현재 위치를 얻어 저장

	tuning = size * 0.8; // 파일의 80% 값 저장

	fseek(inFile, 0, SEEK_SET);	// 파일 포인터를 파일 시작으로 이동
 

파일의 위치를 구하고자 랜덤 접근 함수 fseek()을 사용하였다.

fseek()함수의 원형은 int fseek(FILE* stram, long offset, int start);으로 start부터 offset까지 스트림을 이동 시키며 성공시 0을 반환하고 실패시 0이 아닌 값을 반환한다.

fseek()의 세 번째 인자인 start의 종류는 크게 3가지가 있으며 이는 아래의 표와 같다.

 

표 2 start 종류

기호 상수 설명
SEEK_SET 0 파일의 시작 위치
SEEK_CUR 1 파일의 현재 위치
SEEK_END 2 파일의 끝 위치

 

또 다른 랜덤 접근 함수 ftell()는 현재의 파일 위치가 파일의 시작부터 얼마나 떨어져 있는지 확인할 수 있으며 ftell() 함수의 원형은 long ftell(FILE* stram);으로 파일 포인터 stream의 위치를 확인해주어 성공 시 파일 포인터의 위치를 반환하며 실패시 EOF를 반환해준다. 여기서 EOF란 End Of File의 약자이며 파일의 끝을 의미한다.

 필자는 fseek(inFile, 0, SEEK_END)을 이용하여 파일 포인터를 0에서부터 파일의 끝으로 이동시키고 ftell()함수를 이용하여 파일 포인터의 현재 위치를 저장을 해주었다. 앞서 fseek를 이용하여 파일의 끝으로 이동시켰으므로 즉, 파일의 끝 위치 값을 얻어와 size 변수에 저장하였다.

 

 또한 앞서 실행한 fseek()함수로 현재 스트림이 파일 끝에 위치하고 있어 원할한 작업을 위해 fseek()함수를 다시 한 번 더 이용하여 스트림을 파일 시작 위치로 재이동 시켰주었다.

 

 연구 제한점을 고려했을 때 수정하기 전 파일의 내용을 80%만 읽기 위해 현재 파일의 위치(파일 끝) 값에 0.8을 곱하여 tuning 변수에 저장해주었다.

 

 

// world.txt 파일을 쓰기 전용(w)으로 열기
	outFile = fopen("world.txt", "w");

	while (!feof(inFile)){ // 파일의 끝에 도달할 때까지
		// inFile의 데이터를 읽어 itemsRead에 저장
		itemsRead = fread(data, sizeof(int), count, inFile);

		if(ferror(inFile)){ // 파일 스트림을 읽을 때 오류 발생 시
			// 오류 메시지 출력
			fprintf(stderr, "Read error\n");
		}

 

필자는 새로 파일을 쓰기 위해 fopen()함수의 w모드로 world.txt 파일을 열도록 하였다. world를 이름으로 한 텍스트 파일은 존재하지 않으므로 w모드를 통해 world라는 이름으로 텍스트 파일을 새로 생성한다.

 

 또한 feof()함수를 이용하여 파일의 끝을 확인하도록 하였다. 파일의 끝을 확인하는 방법은 feof()외에도 fgetc(), fgets(), fscanf()와 다양한 함수들이 존재하지만 파일 끝에서 반환되는 값이 각기 달라 일일이 기억하는 것이 불편하므로 feof()함수를 사용하였다.

 

feof()함수의 원형은 int feof(FILE* stream);으로 파일의 끝에 도달했다면 0이 아닌 값을 반환하고 파일의 끝에 도달하지 못한 경우 0을 반환한다.

 

 while문과 feof()함수를 통해 inFile이 끝에 도달할 때까지 앞으로 서술할 내용들을 반복 실행한다.

 fread()함수를 통해 스트림에서 데이터를 읽어 읽은 전체 항목 수를 반환한 값 즉, count값을 itemsRead 변수에 할당해준다.

 

fread()함수의 원형은 size_t fread(void* buffer, suze_t size, size_t count, FILE* stram);으로 첫 번째 인자 buffer는 파일로부터 입력 받은 데이터를 저장하는 버퍼를 가리키는 포인터로 여기서는 사전에 선언한 data를 사용하였다. 두 번째 인자 size는 한 번에 입력 받을 데이터의 바이트 크기이며 세 번째 인자 count는 입력 횟수, 네 번째 인자 stram은 파일 입력 스트림으로 여기서는 inFile의 내용을 읽어 outFile에 써야 하므로 inFile을 사용하였다.

 

if문에 ferror()함수를 이용하여 inFile 스트림을 읽을 때 오류를 테스트하여 오류 발생시 경고 메세지 Read error\n를 출력한다.

 

 

 

		// 버퍼에 저장된 내용을 outFile에 쓴다
		fwrite(data, sizeof(int), itemsRead, outFile);

		if(tuning < size){ //파일의 80%이상이 채워졌으면
			// s1에 저장된 내용(데이터)을 outFiles에 쓴다
			fwrite(s1, sizeof(int), 5, outFiles;
		}
	}
	return 0;
}
 

while문을 통해 inFile의 끝에 도달할 때까지 첫 번째 fwrite()함수를 사용하여 버퍼에 저장된 데이터를 파일에 출력하나 if 조건문을 통해 tuning<size 즉, 20% 이상은 다른 인자를 가지고 있는 두 번째 fwrite()함수를 사용하여 s1에 저장된 Hello, world\n를 입력하도록 구현하였다.

 

 fwrite()함수의 원형은 size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);으로 첫 번째 인자 buffer는 출력 데이터를 저장한 버퍼를 가리키는 포인터이고 두 번째 인자인 size는 한 번에 출력할 데이터의 바이트 크기를 의미한다. 세 번째 인자인 count는 반복 횟수를, 네 번째 인자인 stream은 파일 출력 스트림을 가리킨다.

 

if문 밖의 fwrite()함수에서는 기존의 버퍼를 가리키는 포인터 즉, data를 itemsRead 만큼 반복하여 outFile 스트림을 통해 버퍼에 저장된 데이터를 파일을 출력하고 if문 안의 fwrite() 함수에서는 수정할 내용을 저장한 버퍼를 가리키는 포인터 즉, s1을 5만큼 반복하여 outFile 스트림을 통해 버퍼에 저장된 데이터를 파일에 출력하도록 구현하였다.

 

 


전체 코드

#include <stdio.h> // 표준 입출력 기능 헤더 파일
#include <stdlib.h> // 메모리 할당, 해제 기능 헤더 파일

int main() {
	FILE *inFile; // 파일 스트림 생성을 위한 FILE 포인터
	FILE *outFile; // 파일 스트림 생성을 위한 FILE 포인터

	int itemsRead; // 읽은 데이터 수를 저장하기 위한 변수
	int count = 2048; // 읽힐 항목의 최대 수
	int data[2048]; // 데이터의 스토리지 위치

	char *s1 = "Hello, world!\n"; // 파일 데이터를 수정할 내용

	int size = 0; // 파일의 크기를 저장할 변수
	int tuning = 0; // 파일의 80% 값을 저장할 변수

	inFile = fopen("hello.txt", "r"); // hello.txt 파일을 읽기모드로 열기
	if (!inFile) { // 파일이 NULL 값이라면
		fprintf(stderr, "File open error.Exiting program\n"); // 오류 메세지 출력
		exit(1); // 프로그램 종료
	}
	
	fseek(inFile, 0, SEEK_END); // 파일 포인터를 파일의 끝으로 이동   
	
	size = ftell(inFile); // 파일 포인터의 현재 위치를 받아 저장
	tuning = size * 0.8; // 파일의 80% 값 저장

	fseek(inFile, 0, SEEK_SET); // 파일 포인터를 파일 시작으로 이동

	outFile = fopen("world.txt", "w"); // world.txt 파일을 쓰기 전용으로 열기

	while (!feof(inFile)) { // 파일의 끝에 도달할 때까지
		itemsRead = fread(data, sizeof(int), count, inFile); // inFile의 데이터를 읽어 itemsRead에 저장
		if (ferror(inFile)) { // 파일 스트림을 읽을 때 오류 발생시
			fprintf(stderr, "Read error\n"); // 오류 메세지 출력
		}
		
			fwrite(data, sizeof(int), itemsRead, outFile); // 버퍼에 저장된 내용을 outFile에 쓰기

			if(tuning<size){ // 파일의 80% 이상이 채워졌으면
				fwrite(s1, sizeof(int), 5, outFile); // s1에 저장된 내용(데이터)를 outFiles에 쓰기
            }


	}
	return 0; 
}

 

 


 

 

프로그램 실행 전 hello.txt 파일을 생성해서 프로그램 폴더에 첨부해 두었다.

hello.txt 파일은 아래의 사진으로 1로 가득 채워져 있는 33,792KB 파일이다.

 

 

프로그램 실행 전 hello.txt

 

 

 

 

프로그램 실행 전 폴더 상태는 다음 그림과 같다.

프로그램 실행 전 폴더 상태

 

 

위의 사진들을 기반으로 프로그램 실행 전의 상태를 확인할 수 있다.

 

이 같은 배경에서 프로그램을 실행하면 앞으로 서술한 과정과 같다.

 

프로그램 실행 후 폴더 상태

프로그램을 실행하면 폴더 상태가 변화한 것을 확인 할 수 있다.

프로그램 실행 전과 후의 큰 차이점은 world.txt 파일이 생성된 것이다.
world.txt 파일은 hello.txt 파일과 달리 사용자가 직접 파일을 만들지 않고도 프로그램을 실행하면 자동으로 생성해준다.
world.txt 파일은 hello.txt 파일과 비슷한 크기인 33.879KB를 가지고 있으며 파일 상태는 아래 사진과 같다.

 

 

 

world.txt

보다시피 s1에 파일 데이터를 수정할 내용으로 입력한 Hello, world\n가 중간 중간에 출력된 것을 확인할 수 있다.

 

 

실험 및 결과를 작성하면서 프로그램을 실행하면서 world.txt 파일을 생성해내는데 이 때 hello.txt에는 변화가 없을까라는 생각이 문득 들었다.

프로그램 실행 후 hello.txt 파일을 확인하면 아래 그림으로 실행 전 hello.txt 파일과 실행 후 hello.txt 파일과 동일한 것을 확인 할 수 있다.

프로그램 실행 후 hello.txt

 

 

profile

Devlog

@덩이

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그