c

파일처리

haniru 2025. 11. 13. 23:38

power shell 에서

더보기

wsl -l -v

wsl -d Ubuntu

 

리눅스

더보기

ifconfig

sudo apt install net-tools

ifconfig

sudo apt install apache2

 

ifconfig 후 eth0에 뜨는 ip로 들어가면 아파치가 뜬다

 

cd /var/log

ls # apache가 생긴걸 볼 수 있음

cd apache2

cat access.log # cat access 까지 치고 tab 누르기, 여기에 누가 어떻게 접근했는지 다나온다

 

파일 열기

cd ~/work/basic

ls

code Ex18-1.c

nano ./a.txt # ctrl + S > ctrl + X

cat a.txt

#include <stdio.h>

int main() {
    FILE* fp; // FILE: typedef struct _IO_FILE FILE;
    // fp = fopen("./a.txt", "r"); // ./는 현재 폴더라는 뜻. 생략 가능
    // fp = fopen("a.txt", "r");
    fp = fopen("/home/work/basic/a.txt", "r");
    if (fp == NULL) {
        printf("파일이 열리지 않았습니다.\\n");
        return 1;
    }

    printf("파일이 성공적으로 열렸습니다!!!\\n");

    return 0;
}

 

FILE 은 구조체이다. fopen 함수는 메모리에 스트림 파일을 만들고 프로그램에서 사용할 수 있도록 FILE 구조체 변수의 주소를 반환한다.

 

 

입/출력 스트림

code Ex18-2.c

gcc -o textviewer Ex18-2.c

./textviewer

./textviewer a.txt

./textviewer hello.c

echo $PATH

sudo mv ./textviewer /usr/local/bin # 현재 디렉토리의 textviewer 파일을 /usr/local/bin 디렉토리로 이동

textviewer hello.c

cat hello.c

echo $PATH # 환경변수의 현재 

ls

sudo textviewer /var/log/kern.log # 관리자 권한으로 시스템 파일 읽기 시도

#include <stdio.h>
#include <stdlib.h> // exit, EXIT_FAILURE

// int main(int argc, char** argv)
int main(int argc, char* argv[]) { // main 함수의 매개변수 선언. OS에 따라 **가 잘 안먹는 경우가 있어서 char* argv[]로 써도 됨
    FILE* fp; // FILE: typedef struct _IO_FILE FILE;
    int ch;

    // 명령어 인자가 1개만 실행되지 않게 막아야함
    if (argc < 2) {
        printf("다음과 같이 사용하세요. 사용법: %s <파일명 또는 경로>\n", argv[0]);
        exit(2); // 프로그램 자체를 종료시켜 버림. do while문 써도 되고 이렇게 exit 해도 됨
    }

    // char* path = "/home/robot/work/basic/a.txt";
    // char* mode = "r";

    // fp = fopen("a.txt", "r");
    // fp = fopen(path, mode);
    fp = fopen(argv[1], "r"); // 사용법: textviewer /home/robot/a.txt 
    if (fp == NULL) {
        printf("파일이 열리지 않았습니다\n");
        return EXIT_FAILURE;
    }

    // 파일 내용 읽기 및 출력
    // while ((ch = fgetc(fp)) != EOF) {
    //     putchar(ch);
    // }
    while(1) {
        ch = fgetc(fp); // 한글자를 가져오는 함수
        if (ch == EOF) break;// -1 파일의 끝, 전처리기

        putchar(ch);
        // printf("%c", ch);
    }

    fclose(fp); // 자원 반납
    return 0;
}
경로 구분 예시 의미
표준 리눅스 경로 /usr/local/bin, /usr/bin, /bin 리눅스 시스템에서 기본 명령어나 설치된 애플리케이션의 실행 파일이 위치하는 표준 디렉토리입니다.
WSL/Windows 연결 경로 /mnt/c/Windows/system32, /mnt/c/Windows **WSL (Windows Subsystem for Linux)**이 Windows 운영체제의 경로를 자동으로 가져와서 포함시킨 것입니다. 이를 통해 WSL 터미널 내에서 Windows의 실행 파일(예: cmd.exe, powershell.exe)을 바로 실행할 수 있습니다.
사용자 앱 경로 /mnt/c/Users/.../VS Code/bin 사용자가 설치한 프로그램(예: VS Code, JetBrains Toolbox)의 실행 경로가 포함되어 있어, 해당 프로그램의 명령어를 어디서든 사용할 수 있습니다.

 

스트림 파일이란 데이터가 프르는 통로이다. OS 레벨 데이터를 읽기 위해선 하드디스트의 섹터 번호, 메모리 주소, 장치 드라이버등을 복잡하게 다뤄야 하지만 C 개발자는 복잡한 하드웨어 구조를 알 필요 없이 단순히 FILE* 포인터를 사용하여 fgetc(), fputc(), fprintf()와 같은 표준 함수만 호출하면 된다. fputc(ch, fp), fputc(ch, stdout) 이 두 코드는 fp 와 stdout 이 서로 다른 대상을 가리키지만, 개발자는 동일한 함수 fputc 를 사용한다 (일관성)

 

파일 개방을 통해 만들어진 스트림 파일은 메모리를 사용한다. 따라서 파일 입출력이 끝나면 이들을 회수해 재활용하기 위해 파일을 닫아야 한다. 또한 스트림 파일에 남아 있는 중요한 데이터가 장치에 기록되기 전에 시스템 사고로 지워질 수 있으므로 사용이 끝난 파일은 즉시 닫아 스트림 파일의 데이터를 장치에 기록하는 것이 좋다.

 

 

문자 출력

한 문자를 파일로 출력할 때는 fputc 함수를 사용한다.

#include <stdio.h>

int main() {
    FILE* fp;
    char str[] = "banana";

    fp = fopen("b.txt", "w");
    if (fp == NULL) { // fopen의 정상동작 유무 확인
        printf("파일을 만들지 못했습니다.\n");
        return 1;
    }
    int i=0;
    while(str[i] != '\0') {
        // fputc(str[i], fp);
        fprintf(fp, "%c", str[i]);
        i++;
    }
    fputc('\n', fp);
    fclose(fp);

    return 0;
}

 

 

 

#include <stdio.h>

int main() {
    FILE *fp;
    char* fruit = "블루베리";
    fp = fopen("fruits.txt", "w");

    if (fp == NULL) {
        printf("파일을 만들지 못했습니다\n");
        return 1;
    }

    int i=0;
    while (fruit[i] != '\0') {
        fprintf(fp, "%c", fruit[i]);
        i++;
    }
    fputc('\n', fp); // \n 대신 \0을 쓰면 안됨
    fclose(fp);

    return 0;
}

 

 

표준 입출력 스트림 파일

운영체제는 프로그램을 실행할 때 기본적으로 3개의 스트림 파일을 만든다. 그리고 이들을 키보드와 모니터 등에 연결해서 입출력 함수들이 파일 포인터 없이 사용할 수 있도록 제공한다.

ch = fgetc(stdin): 표준입력(stdin, 즉 키보드)에서 한 문자를 읽어온다, ch = getchar() 와 동일

fputc(ch, stdout): 읽어온 문자 ch 를 표준 출력 (stdout, 즉 모니터)로 보낸다, puchar(ch) 와 동

if (ch == EOF) break: 입력의 끝을 감지하여 반복문을 종료합니다

 

stdin: 키보드, stdout: 모니터, stderr: 모니터

#include <stdio.h>

int main()
{
    int ch;

    while (1)
    {
        // ch = getchar();
        ch = fgetc(stdin); // 키보드 (파일 포인트)

        if (ch == EOF) // wsl ubuntu ^z -> ^c로 종료했음
            break; // indent

        // putchar(ch);
        fputc(ch, stdout); // 모니터 (파일 포인트)
    }

    return 0;
}

 

 

텍스트모드와 바이너리 모드의 차이점.

파일 입출력시 발생하는 캐리지 리턴 변환 문제:

출력 (쓰기) - 코드에서는 putc('\n', fp) 이지만 window 파일에서는 \r\n (2바이트)로 변환되어 저장된다

입력 (읽기) - 코드에서는 fgetc(fp)가 \n을 반환하지만 window 파일에서는 \r\n (2바이트) 을 \n으로 변환하여 반환한다

vi a.txt 나 cat a.txt 하면 a.txt 에 있는 파일 내용을 문자열로 간주하고 아스키 코드표에 따라 문자 또는 제어 명령으로 해석해 이상한 출력이 나온다. 하지만 printf("%4d", res)는 읽어온 바이트 값을 아스키 문자로 해석하지 않고 10진 정수 그 자체로 변환하여 화면에 출력한다

 

"w" 모드로 파일을 열고 fputc를 사용하면, C 런타임 라이브러리가 파일 쓰기 전에 데이터를 필터링하고 변환하지만 "wb" 모드는 제어문자가 그대로 남는다 (근데 아스키 코드 33번 이후 값이 들어가면 "w" 모드, "wb"모드 둘다 결과가 동일하게 잘 나온다)

#include <stdio.h>

int main() {
    FILE* fp;
    int ary[10] = {13, 10, 13, 13, 10, 26, 13, 10, 13, 10};
    int i, res;

    fp = fopen("a.txt", "wb"); // 바이너리 파일로 개방
    for (i=0; i < 10; i++) {
        fputc(ary[i], fp);
    }
    fclose(fp);

    fp = fopen("a.txt", "rt"); // 같은 파일을 텍스트 파일로 개방
    while (1) {
        res = fgetc(fp);
        if (res == EOF) break;
        printf("%4d", res);
    }
    fclose(fp);

    return 0;
}

 

1. 쓰기 (wb 모드): "wb" (바이너리 쓰기) 모드는 CR/LF 변환을 하지 않는다. 아스키 코드값 그대로 파일에 10바이트로 기록한다.

2. 읽기 (rt 모드): "rt" (텍스트 읽기) 모드는 CR/LF 변환을 시도한다. Windows 에서 바이너리로 저장된 데이터 중 아스키 코드 13인 CR (\r) 값이 있다면, C 라이브러리는 이를 무시하고 다음 바이트를 읽으려 시도한다.

hexdump: 파일의 실제 바이트 데이터를 16진수, 8진수, 10진수 등 다양한 형식으로 보여주어 텍스트 모드 변환이 실제로 발생했는지, 파일에 어떤 제어 문자가 있는지 확인하는 데 사용된다/

hexdump -v -d a.txt # 파일의 내용을 부호없는 10진수 형식으로 덤프한다 (실제 바이트 값)

hexdump -c a.txt # 파일의 내용을 문자 형식으로 덤프한다 (제어 문자)

 

 

텍스트파일과 바이너리 파일의 차이

구분 텍스트 모드 ("r", "w", "rt", "wt") 바이너리 모드 ("rb", "wb")
변환 여부 O (운영체제에 맞게 변환) X (바이트 그대로 입출력)
개행 문자(\n) 입출력 시 변환됩니다.

Windows: \n $\leftrightarrow$ CR+LF (\r\n)

Linux/Unix: \n $\leftrightarrow$ LF (\n)
변환 없이 오직 LF (\n, 아스키 코드 10) 한 바이트로 처리됩니다.
용도 사람이 읽을 수 있는 문서 파일 (소스 코드, 한글, 아스키 텍스트) 실행 파일(.exe, .bin), 압축 파일(.zip), 이미지 파일 등
데이터 타입 문자열, 아스키 코드 모든 종류의 2진 데이터

 

 

파일 읽기 쓰기

fputc 나 fprintf 를 호출하면, 데이터는 먼저 C 라이브러리가 관리하는 메모리 버퍼에 쌓인다.

이 버퍼가 가득 찼거나 \n 문자를 만났거나 fflush() 또는 fclose()를 호출했을 때 버퍼의 모든 데이터가 한 번에 운영체제에게 전달된다

 

일반 텍스트: fputs, fprintf

바이너리 블록(구조체나 배열): fwrite

문자 단위: fputc

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE* fin;
    int ch;
    char str[80];

    fin = fopen("read.txt", "r");
    
    if (fin == NULL) {
    	return 1;
    }
    
    int i=0;

    while((ch = fgetc(fin)) != EOF) {
        str[i] = (char)ch;
        i++;
    }
    str[i] = '\0';
    fclose(fin);

    FILE* fout;
    fout = fopen("write.txt", "w");
    fprintf(fout, "%s", str);
    fclose(fout);

    return 0;
}
#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE* fin, *fout;
    int ch;

    fin = fopen("read.txt", "r");

    if (fin == NULL) {
        printf("read.txt가 열리지 않았습니다");
        exit(1);
    }

    fout = fopen("write.txt", "w");

    while ((ch = fgetc(fin)) != EOF) {
        fputc(ch, fout);
    }

    fclose(fin);
    fclose(fout);

    return 0;
}

 

개방모드: fseek, rewind, feof

하단 코드에서 fseek 함수를 꼭 호출해야 하는데, 입출력 순서가 꼬이기 때문이다

버퍼의 데이터를 하드디스크로 옮기고 버퍼를 읽기 위한 공간으로 설정한 후 하드디스크의 데이터를 처음부터 다시 읽도록 해야한다

fseek(fp, 0, SEEK_SET) = rewind(fp)

#include <stdio.h>
#include <string.h>

int main() {
    FILE *fp;
    char str[20];

    fp = fopen("fruit.txt", "a+"); // a+ 모드: 읽기와 쓰기 모두 가능. 커서는 파일 맨 앞에서 시작
    if (fp == NULL) {
        printf("파일을 만들지 못했습니다.");
        return 1;
    }

    while (1) {
        printf("\n과일 이름 또는 명령 입력 > ");
        if (scanf("%s", str) != 1) {
            break;
        }

        if (strcmp(str, "end") == 0) {
            break;
        } else if (strcmp(str, "list") == 0) {
            rewind(fp);
            while(fgets(str, sizeof(str), fp) != NULL) {
                printf("%s", str);
            }
            clearerr(fp);
        } else {
            fprintf(fp, "%s\n", str);
            printf("%s 파일에 추가되었습니다\n", str);
        }
    }
    printf("파일을 닫고 프로그램을 종료합니다\n");
    fclose(fp);

    return 0;
}

 

파일열기: fopen, 파일 닫기: fclose, 문자 입력: fgetc, 문자 출력: fputc

 

여러줄의 문장을 입력해 한줄로 출력

at.txt 는 여러줄 문장으로 이루어져 있어서 res = fgets(str, sizeof(str), in) 으로 한 줄씩 문자를 읽고, 읽어들인 문자열에서 개행 문자를 널 문자로 치환해 b1.txt 파일에 넣는다.

#include <stdio.h>
#include <string.h>

int main() {
    FILE *in, *out;
    char str[80];
    char *res;

    in = fopen("a1.txt", "r");

    if (in == NULL) {
        printf("파일 읽기에 실패했습니다");
        return 1;
    }

    out = fopen("b1.txt", "w");

    while(1) {
        res = fgets(str, sizeof(str), in);
        if (res == NULL) break;
        
        str[strlen(str) - 1] = '\0';

        fputs(str, out);
        fputs(" ", out);
    }
    fclose(in);
    fclose(out);

    return 0;
}

'c' 카테고리의 다른 글

입출력, 매크로, wsl  (0) 2025.11.17
c 언어 문제  (0) 2025.11.14
구조체  (0) 2025.11.12
배열, 포인터와 구조  (0) 2025.11.11
이중포인터 N차원 배열  (1) 2025.11.10