C언어 문제/명렁어 구현
[wc] 명령어 구현
자연&사람
2020. 7. 1. 18:19
반응형
wc (word count?)명령어는 파일에 대한 라인수, 단어수, 파일의 크기를 출력하는 프로그램입니다. 라인수는 new line으로 분리된 수이며, 단어수는 space문자로 구분되는 문자열의 갯수입니다. wc의 기능을 완벽하게 구현한 것은 아니며 simple_wc라고 이름지었습니다. (전체 소스는 최하단에 있는 첨부파일을 다운로드바랍니다.)
1. 전역 변수/상수 및 타입정의
/*==========================================================
이 프로그램은 UNIX/LINUX의 wc 명령어와 비슷한 역할을 하는 프로그램입니다.
수정 / 배포 가능합니다.
copyright(c) by www.it-note.kr 2020.07.01
===========================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <libgen.h>
#include <ctype.h>
#define OPTION_ALL 0xffffffff
#define OPTION_LINE_COUNT 1
#define OPTION_WORD_COUNT 2
#define OPTION_FILE_SIZE 4
int option = 0;
/*
파일의 정보를 저장하는 구조체
*/
typedef struct wc_info
{
char filename[1024];
long line_count;
long word_count;
long file_size;
} wc_info_t;
2. 파일단위 라인수, 단어수, 파일의 크기 계산 및 출력
/*
* 파일을 분석하여 단어수, 라인수, 파일의 크기를 분석하는 함수
* 파라미터로 파일명을 넘기지 않고 FILE *를 넘긴 이유는 파일의 입력이 없는 경우
* 표준 입력에서 입력을 받기 때문임
*/
int word_count(FILE *fp, wc_info_t *info)
{
char line[8096];
char *tmp;
int size, idx;
int prev_space = 1;
memset(info, 0x00, sizeof(wc_info_t));
info->line_count == 1;
while((size = fread(line, 1, sizeof(line), fp)) != 0) {
for(idx = 0; idx < size; idx++) {
/* 파일의 라인수를 계산하는 로직 */
if(line[idx] == '\n') {
info->line_count++;
}
/* 파일의 단어수를 계산하는 로직 */
if(prev_space && !isspace(line[idx])) {
info->word_count++;
prev_space = 0;
} else if(isspace(line[idx])) {
prev_space = 1;
}
}
}
/* 파일 크기를 얻는 부분 */
info->file_size = ftell(fp);
if(info->file_size == 0) {
info->line_count = 0;
}
}
/*
각각의 데이터의 합을 계산하고, 최대 큰 값의 데이터 폭을 return함
*/
int summary(wc_info_t *sum, const wc_info_t *info, int count)
{
int idx;
memset(sum, 0x00, sizeof(wc_info_t));
char size[32];
strcpy(sum->filename, "total");
for(idx = 0; idx < count; idx++) {
sum->line_count += info[idx].line_count;
sum->word_count += info[idx].word_count;
sum->file_size += info[idx].file_size;
}
if(option == OPTION_LINE_COUNT) {
sprintf(size, "%ld", sum->line_count);
} else if(option == OPTION_WORD_COUNT) {
sprintf(size, "%ld", sum->word_count);
} else {
sprintf(size, "%ld", sum->file_size);
}
return strlen(size);
}
/*
option에 따라서 출력 여부가 결정되지만, 항상 라인수, 단어수, 파일크기 순으로 출력함
*/
void print_info(const wc_info_t *info, int width)
{
if(option == 0) {
option = OPTION_ALL;
}
if(option & OPTION_LINE_COUNT) {
printf("%*ld ", width, info->line_count);
}
if(option & OPTION_WORD_COUNT) {
printf("%*ld ", width, info->word_count);
}
if(option & OPTION_FILE_SIZE) {
printf("%*ld ", width, info->file_size);
}
printf("%s\n", info->filename);
}
3. Option에 대한 처리 부분
void version(char *pgm)
{
printf("%s version 1.0.0\n", basename(pgm));
}
void help(char *pgm)
{
printf("Usage: %s [OPTIOS]... [FILES]...\n", basename(pgm));
printf("line수, word수, file크기, 파일명을 출력하고 파일이 2개 이상이면 합계정보를 출력합니다.\n");
printf("만약 파일을 입력하지 않으면 표준입력으로부터 데이터를 입력받습니다.\n");
printf(" -c : 파일의 크기(byte수)를 출력합니다.\n");
printf(" -l : 라인수를 출력합니다.\n");
printf(" -w : 단어수를 출력합니다.\n");
printf(" --help : 도움말을 출력하고 프로그램을 종료합니다.\n");
printf(" --version : 버전정보를 출력하고 프로그램을 종료합니다.\n\n");
}
int check_arg(int argc, char **argv)
{
int idx;
int count = 0;
for(idx = 1; idx < argc; idx++) {
if(strcmp(argv[idx], "--help") == 0) {
help(argv[0]);
exit(0);
} else if (strcmp(argv[idx], "--version") == 0) {
version(argv[0]);
exit(0);
} else if(argv[idx][0] == '-') {
if(strcmp(argv[idx], "-c") == 0){
option |= OPTION_FILE_SIZE;
} else if(strcmp(argv[idx], "-l") == 0) {
option |= OPTION_LINE_COUNT;
} else if(strcmp(argv[idx], "-w") == 0) {
option |= OPTION_WORD_COUNT;
} else {
fprintf(stderr, "invalid option: %s\n", argv[idx]);
help(argv[0]);
exit(1);
}
} else {
count++;
}
}
return count;
}
4. Main 함수
int main(int argc, char **argv)
{
FILE *fp;
wc_info_t *infos = NULL;
wc_info_t sum;
int idx, width;
int file_count = 0;
int cur_file = 0;
file_count = argc;
if(argc > 1) {
file_count = check_arg(argc, argv);
}
/* 명령어만 입력하고 실행한 경우 */
if(file_count == 0) {
infos = (wc_info_t *)malloc(sizeof(wc_info_t));
word_count(stdin, &infos[cur_file]);
cur_file++;
} else {
infos = (wc_info_t *)malloc(sizeof(wc_info_t) * file_count);
for(idx = 1; idx < argc; idx++) {
if(argv[idx][0] == '-') {
continue;
}
if((fp = fopen(argv[idx], "r")) == NULL) {
fprintf(stderr, "%s file open error: %s\n", argv[idx], strerror(errno));
continue;
}
word_count(fp, &infos[cur_file]);
strcpy(infos[cur_file].filename, argv[idx]);
cur_file++;
fclose(fp);
}
}
width = summary(&sum, infos, cur_file);
for(idx = 0; idx < cur_file; idx++) {
print_info(&infos[idx], width);
}
if(cur_file > 1) {
print_info(&sum, width);
}
free(infos);
return 0;
}
소스 파일
반응형