반응형

 

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;
}

 

 

 

소스 파일

simple_wc.c
0.01MB

반응형
블로그 이미지

자연&사람

행복한 개발자 programmer since 1995.

,