해결된 질문
작성
·
29
1
#define CRTSECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int ch;
FILE* fr; // TODO: file pointer to write
FILE* fw;
const char* out_filename = "output.txt";
unsigned long count = 0;
if (argc != 3)
{
printf("Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
if ((fr = fopen(argv[1], "r")) == NULL) // Open a text file for reading.
{
printf("Can't open %s\n", argv[1]);
exit(EXIT_FAILURE);
}
if ((fw = fopen(argv[2], "w")) == NULL)
{
printf("Can't open %s\n", argv[2]);
exit(EXIT_FAILURE);
}
/*
r: reading
w: creating and writing or over writing
a: appending or creating and writing
r+: both reading and writing
w+: reading and writing, over writingor creaitng
a+: reading and writing, appending or creating
*/
while ((ch = fgetc(fr)) != EOF) // getc(fr)
{
fputc(ch, stdout);
fputc(ch, fw);
count++;
}
printf("\n");
fclose(fr);
fclose(fw);
printf("File %s has %lu characters\n", argv[1], count);
printf("Copied to %s", out_filename);
return 0;
}
질문) 출력 조건문에서 out_filename대신 argv[2]를 대신 사용하려면 printf("Usage: %s filename\n", argv[0]); 이 부분 조건문 부분의 인자를 3으로 해야 안전하게 데이터를 보호할 수 있는 것으로 알고 있는데 이렇게되면 파일이 없는 경우 생성이 되기전에 프로그램이 종료가 되는 문제점이 발생합니다. 이 상황에서 argv[2]를 사용하려고 하면 코드를 어떻게 구성해야 프로그램이 정상작동될까요?
답변 1
2
안녕하세요? 질문&답변 도우미 Soobak 입니다.
좋은 고민이시네요.
우선, 말씀해주신 내용 중 '파일이 없는 경우 생성되기 전에 프로그램이 종료가 되는 문제점이 발생' 부분에서 '파일이 없는 경우' 라는 표현보다는, 'argv[2]
인수가 없는 경우', 즉, '출력하려는 파일의 이름'이 인수로 전달되지 않은 경우 로 생각해보시면, 해결 방법을 떠올리시는 것이 보다 편하실 것 같습니다.
문제는 다양한 방법으로 해결할 수 있을 것 같습니다.argv[2]
를 활용하여 출력 파일의 이름을 반드시 지정하도록 변경하시는 것이 의도이시라면,
말씀해주신 내용처럼 argc
가 3
이 아닌 경우에 대해서 Usage: 프로그램이름(argv[0]) filename
을 출력하고 프로그램을 종료하는 부분을 Usage: 프로그램이름(argv[0]) 읽을파일이름 출력할파일이름
등과 같이 수정하여, 사용자가 argv[2]
도 함께 입력해야 됨을 알 수 있도록 사용자 친화적으로 변경하는 방법이 있을 것 같습니다.
또는 일종의 '기본값' 설정처럼, argc
가 2
인 경우, 즉, 읽어들일 파일의 이름만 인수로 전달된 경우에는 현재와 같이 "output.txt"
문자열을 생성 파일의 이름으로 사용하되, argc
가 3
인 경우, 즉, 출력 파일의 이름 또한 인수로 전달된 경우에는 "output.txt"
대신 argv[2]
를 생성 파일의 이름으로 사용하도록 구성할 수도 있을 것 같습니다.
네, 잘 이해하시고 작성하셨습니다.
강의의 내용에서 더 나아가 능동적으로 재밌게 학습하시는 것 같아, 저 또한 재미를 느끼네요. 감사합니다.
우선, 첨부해주신 코드를 컴파일 후 실행해보며 여러가지 경우를 체크해보았습니다.
이를 바탕으로, 문제가 발생할 '수' 도 있는 상황에 대해서 말씀드려봅니다.
const char* out_filename
에서의 자료형에 관한 문제입니다.
이렇게 const
로 한정한 포인터 변수에 대해서 추후 argv[2]
를 할당하는 것은 C언어에서 '문법적으로' 문제가 되지는 않지만, const
로 한정하는 것의 의미에 일치하지 않으며 추후 혼동을 일으킬 수 있으므로 권장되지 않는 방식입니다. (이 부분은 강의 10.5 포인터의 호환성 의 17:50 부분의 교수님 설명을 참고해보시면 이해에 도움이 많이 되실 것 같습니다.)
따라서, chsr* out_filename
을 선언하고, 문자열을 동적으로 할당해주는 것이 보다 더 좋은 코드라고 생각합니다.
이미 훌륭하게 작성하셨기에, 이해를 조금 도와드리기 위한 예시 코드를 첨부드려봅니다.
이해를 위해 답변드렸던 이전 답변의 내용의 일관성을 위하여, 로그 부분은 영어가 아닌 한글로 통일하여 작성하였습니다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 4096
#define DEFAULT_OUT_FILENAME "output.txt"
int main(int argc, char* argv[])
{
FILE* fr;
FILE* fw;
char buffer[BUFFER_SIZE];
size_t bytes_read;
char* out_filename;
unsigned long count = 0;
if (argc < 2 || argc > 3)
{
fprintf(stderr, "사용법: %s 읽을파일이름 [출력할파일이름]\n", argv[0]);
exit(EXIT_FAILURE);
}
if ((fr = fopen(argv[1], "r")) == NULL)
{
fprintf(stderr, "%s 파일을 열 수 없습니다\n", argv[1]);
exit(EXIT_FAILURE);
}
if (argc == 3)
{
out_filename = strdup(argv[2]);
}
else
{
out_filename = strdup(DEFAULT_OUTPUT);
}
if (out_filename == NULL)
{
fprintf(stderr, "문자열 메모리 할당 실패\n");
fclose(fr);
exit(EXIT_FAILURE);
}
if ((fw = fopen(out_filename, "w")) == NULL)
{
fprintf(stderr, "%s 파일을 열 수 없습니다\n", out_filename);
free(out_filename);
fclose(fr);
exit(EXIT_FAILURE);
}
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, fr)) > 0)
{
fwrite(buffer, 1, bytes_read, stdout);
fwrite(buffer, 1, bytes_read, fw);
count += bytes_read;
}
printf("\n");
fclose(fr);
fclose(fw);
printf("%s 파일은 %lu 바이트입니다\n", argv[1], count);
printf("%s 파일로 복사되었습니다\n", out_filename);
free(out_filename);
return 0;
}
정확히 이해한 것인지 모르겠지만 수정해보았습니다.
#define CRTSECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int ch;
FILE* fr; // TODO: file pointer to write
FILE* fw;
const char* out_filename = "output.txt";
unsigned long count = 0;
if (argc < 2 || argc > 3)
{
printf("Usage: %s 읽을파일이름 [출력할파일이름]\n", argv[0]);
exit(EXIT_FAILURE);
}
if ((fr = fopen(argv[1], "r")) == NULL) // Open a text file for reading.
{
printf("Can't open %s\n", argv[1]);
exit(EXIT_FAILURE);
}
if (argc == 3)
{
out_filename = argv[2];
}
if ((fw = fopen(out_filename, "w")) == NULL)
{
printf("Can't open %s\n", out_filename);
exit(EXIT_FAILURE);
}
/*
r: reading
w: creating and writing or over writing
a: appending or creating and writing
r+: both reading and writing
w+: reading and writing, over writingor creaitng
a+: reading and writing, appending or creating
*/
while ((ch = fgetc(fr)) != EOF) // getc(fr)
{
fputc(ch, stdout);
fputc(ch, fw);
count++;
}
printf("\n");
fclose(fr);
fclose(fw);
printf("File %s has %lu characters\n", argv[1], count);
printf("Copied to %s", out_filename);
return 0;
}