6 분 소요

계절학기-후반부 스케줄

이수 과정

1_15.파일처리 - 텍스트파일(1)

2_11. 포인터 기초(1)


3_11.포인터 기초(2)

4_16. 동적할당(1)


5_14. 함수와 포인터 활용


6_12. 문자와 문자열


7_13. 구조체와 공용체


8_15. 파일처리 - 이진파일(2)

9_16. 동적할당(2) - 연결리스트, 전처리

시험관련

16일날 시험봄

필기 50분

실기 2시간

하 ㅅㅂ… 족보부터 돌려야 겠다.

파일처리(1)

이름과 성적 정보 내용으로 간단한 파일(basic.txt) 생성하기

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    FILE* f;
    char* fname = "basic.txt";

    char name[30] = "손혜진";
    int point = 100;
    f = open(fname, "w");
    if (f == NULL)
    {
        printf("파일이 열리지 않습니다.\n");
        exit(1); //프로그램 종료
    }
    
    fprintf(f, "이름 %s 학생의 성적은 %d 입니다.\n", name, point);
    fclose(f);

    
    printf("이름 %s 학생의 성적은 %d 입니다.\n");
    puts("프로젝트 폴더에서 파일 basic.txt를 메모장으로 열어보세요.");

    return 0;
}
#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS
#define MAX_LEN 100
#include <stdio.h>

int main(void)
{
    FILE* fs;
    fs = fopen("basic.txt", "r");
    char str[MAX_LEN];
    fgets(str, MAX_LEN, fs);//파일의 내용을 데이터로 읽어서 버퍼에 넣어줌
    printf("%s", str);//버퍼에 있는 데이터를 콘솔에 출력
    return 0;
}

코드리뷰::

Q1. #pragma warning(disable:4996)는 왜 썼는지?

Q2. fgets은 왜썼는지?

콘솔창에서 데이터를 출력해줄때, 해당 데이터를 버퍼에서 긁어오는 방식이라서 버퍼에 데이터를 채워주어야 함. 이 동작을 fgets()가 해줌.

image-20240108113002335

이슈: 파일에 한글이 있을때만 깨져서 출력됨.

이는 인코딩 방식의 설정을 잘못해줘서 발생한 문제이다. 영어만 쓸꺼면 UTF-8을 사용해주면 되지만, 그 외에 한글도 쓰고 싶다면 둘다 가능한 ANSI방식으로 인코딩처리방식을 변환해주어야 한다.

FILE* fs에서 이 FILE이라는 구조체는 뭘까?

라이브러리를 뒤져보니 라는 헤더파일에 있었다. 근데 그러면 왜 이거를 쓸뗴는 해당 헤더파일을 가져올 필요가 없는 걸까? 기본적으로 무조건 가져오는 헤더파일이라서 따로 가져올 필요가 없는 건가? 아니면 헤더파일안에서 위 헤더파일도 가져오도록 이미 물려있는 건가?

//corecrt_wstdio.h
//....
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Stream I/O Declarations Required by this Header
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#ifndef _FILE_DEFINED
    #define _FILE_DEFINED
    typedef struct _iobuf
    {
        void* _Placeholder;
    } FILE;
#endif

이름과 성적 정보 내용으로 간단한 파일(basic.txt) 생성하기(최종본)

#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS
#define MAX_LEN 100
#include <stdio.h>

int main(void)
{
    FILE* fs;
    char* fname = "basic.txt";
    fs = fopen(fname, "r");
    //예외처리
    if (fs == NULL)
    {
        printf("파일이 열리지 않습니다.\n");
        exit(1);
    }
    //파일에 read(before)
    char str[MAX_LEN];
    fgets(str, MAX_LEN, fs);
    printf("파일에 적혀있던 내용입니다.>\n", str);
    printf("%s", str);

    //파일에 write
    char name[30] = "손혜진";
    int score = 100;
    fprintf(fs, "이름: %s  학생의 성적: %d\n", name, score);
    fclose(fs);

    //파일에 write(after)
    printf("\n\n새롭게 편집된 내용입니다>\n");
    printf("이름 %s 학생의 성적은 %d입니다.\n", name, score);
    puts("\n프로젝트 폴더에서 파일 basic.txt를 메모장으로 열어보세요.\n");
    
    return 0;
}

result>

파일에 적혀있던 내용입니다.> 학생입니다

새롭게 편집된 내용입니다> 이름 손혜진 학생의 성적은 100입니다.

프로젝트 폴더에서 파일 basic.txt를 메모장으로 열어보세요.

#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS
#define MAX_LEN 100
#include <stdio.h>

int main(void)
{
    FILE* f;

    f = fopen("myinfo.txt", "w");
    if (f == NULL)
    {
        printf("파일이 열리지 않습니다.\n");
        exit(1);
    }

    char tel[15] = "010-3018-3824";
    char add[30] = "서초구 대정로 557";
    int age = 22;

    //파일 "basic.txt"에 쓰기
    fprintf(f, "전화번호: %s, 주소: %s, 나이: %d\n", tel, add, age);

    fclose(f);
    printf("전화번호: %s, 주소: %s, 나이: %d\n", tel, add, age);
    puts("프로젝트 폴더에서 파일 myinfo.txt를 메모장으로 열어 보세요.");


    return 0;
}

※고찰

애초에 해당 파일을 생성해야만 거기다가 적을 수 있는 줄 알았는데, 그게 아니라, 그냥 입력데이터를 버퍼에 넣고 파일을 알아서 생성하고 버퍼에 저장된 내용을 발로 텍스트 파일에 넣어주는 것 같다.

한개의 이름과 두개의 성적을 입력해 그 내용을 파일에 쓰고 다시 읽어내기

#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS
#define MAX_LEN 100
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char fname[] = "grade.txt";
    FILE* f;
    char name[30];
    int point1, point2, cnt = 0;

    if (fopen_s(&f, fname, "w") != 0)
    {
        printf("파일이 열리지 않습니다. \n");
        exit(1);
    }

    printf("이름과 성적(중간, 기말)을 입력하세요.\n");
    scanf("%s %d %d", name, &point1, &point2);
    fprintf(f, "%d %s %d\n", ++cnt, name, point1, point2);
    fclose(f);

    if (fopen_s(&f, fname, "r") != 0)
    {
        printf("파일이 열리지 않습니다.\n");
        exit(1);
    }

    //파일 "grade.txt"에서 읽기
    fscanf(f, "%d %s %d %d\n", &cnt, name, &point1, &point2);

    fprintf(stdout, "\n%6s%16s%10s%8s\n", "번호", "이름", "중간", "기말");
    fprintf(stdout, "%5d%18s%8d%8d\n", cnt, name, point1, point2);
    fclose(f);

    return 0;
}

이름과 성적(중간, 기말)을 입력하세요. 윤성웅 98 87

번호 이름 중간 기말 1 윤성웅 98 87

다양한 키워드!

feof(FILE* stream): 파일의 끝에 도달했는지 여부를 확인할

파일의 끝에 도달하면 1을 반환, 그렇지 않으면 0 반환

fgets(): 데이터를 라인단위로 읽어들임

get관련 정리

1.getchar()

2.fgets()

put관련 정리

1.putchar()

2.fputs()

여러 줄에 걸쳐 이름, 성적을 입력하여 지정된 이름의 파일에 그 내용을 모두 저장

#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS
#define MAX_LEN 100
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char  fname[] = "grade.txt";
    char names[80];
    int cnt = 0;
    FILE* f;

    if (fopen_s(&f, fname, "w") != 0)
    {
        printf("파일이 열리지 않습니다.\n");
        exit(1);
    };
    printf("이름과 성적(중간,기말)을 입력하세요.\n");
    fgets(names, 80, stdin);

    //콘솔에 이름 중간 기말 입력하고 Enter 치고
    //계속 여러 줄에 걸쳐 여러 학생의 성적을 입력하다가
    //종료하고 싶을 때 새로운 줄 처음에 ctrl + z누르고 Enter 치면 종료

    while (!feof(stdin))
    {   //파일 "grade.txt"에 쓰기
        fprintf(f, "%d ", ++cnt);
        fputs(names, f);
        fgets(names, 80, stdin);
    }

    fclose(f);
    return 0;
}

이름과 성적(중간,기말)을 입력하세요. 윤성웅 89 100 최준수 60 100 김민수 98 89 ^Z

위와 같이 콘솔창에 입력하면(^Z는 콘솔창 종료) grade.txt라는 파일이 생성되고 거기에 해당 내용이 저장된다.

참고로, 위 while문은 아래와 같이 조건형태를 바꿔볼수도 있다.

while (fgets(names, 80, stdin))
    {   //파일 "grade.txt"에 쓰기
        fprintf(f, "%d ", ++cnt);
        fputs(names, f);
        fgets(names, 80, stdin);
    }

위와 같이 바꿀수 있는 이유에 대해서는 공식문서를 참고하자!(링크)

image-20240108104338293

좋은 코드를 레퍼런스하려고 신경쓰자!

지정한 파일 “05flist.c”(c파일)의 내용을 행 번호를 붙여 표준출력으로 그대로 출력하는 프로그램

#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS
#define MAX_LEN 100
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE* f;
    if (fopen_s(&f, "05flist.c", "r") != 0)
    {
        printf("파일이 열리지 않습니다. \n");
        exit(1);
    }
    int ch, cnt = 0;
    printf("%4d: ", ++cnt);
    while ((ch = fgetc(f)) != EOF)
    {
        putchar(ch);
        if (ch == '\n')
            printf("%4d: ", ++cnt);
    }
    printf("\n");
    fclose(f);

    return 0;
}

위 코드를 실행해보면 다음과 같이 텍스트 c파일의 코드들으 모조리 가져와 주는 것을 알수가 있었다.

image-20240108105501540

더 나아가서 c언어로 텍스트 에디터도 만들수 있지 않을까 하는 생각이 든다.그러면?

  1. 엘리베이터 만들기
  2. 테트리스 만들기
  3. C언어로 Editor만들기

이렇게 될수가 있겠다 ㅋㅋㅋ

문자를 숫자로 바꿔주는 법

‘1’-‘0’ -> (int)1

‘0’-‘0’ -> (int)0

파일에서 읽은 문자를 가지고 대소문자 변환후 다른파일에 써보기

//main.c
#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int main(void)
{
    FILE* f1, * f2;
    if ((f1 = fopen("main.c", "r")) == NULL)
    {
        printf("cannot open this file \n");
        exit(1);
    }
    if ((f2 = fopen("convertchar.c", "w")) == NULL)
    {
        printf("cannot open this file\n");
        fclose(f1);
        exit(1);
    }

    char a;
    while ((a = getc(f1)) != EOF)
    {
        if (isalpha(a))
            if (islower(a))
                a = toupper(a);
            else if (isupper(a))
                a = tolower(a);
        putc(a, f2); //fputc(a, f2)
    }

    fclose(f1);
    fclose(f2);
    printf("File convertchar.c is created!!!\n");

    return 0;
}

고찰: 처음에 파일 이름은 main.c인데, if ((f1 = fopen("main.c", "r")) == NULL)부분에서 실행코드의 파일명을 잘못 기입하여 디버그 에러가 발생했다.

코드의 본래의도를 파악하지 못해서 발생한 이슈였다. 코드리뷰를 해보자면, 현재 실행파일의 코드를 긁어와서

그래서 convertchar.c파일을 확인해보면 모두 대문자로 바뀌어 있는 것을 알수가 있다.

//convertchar.c
#PRAGMA WARNING(DISABLE:4996)
#INCLUDE <STDIO.H>
#INCLUDE <STDLIB.H>
#INCLUDE <CTYPE.H>

INT MAIN(VOID)
{
    file* F1, * F2;
    IF ((F1 = FOPEN("MAIN.C", "R")) == null)
    {
        PRINTF("CANNOT OPEN THIS FILE \N");
        EXIT(1);
    }
    IF ((F2 = FOPEN("CONVERTCHAR.C", "W")) == null)
    {
        PRINTF("CANNOT OPEN THIS FILE\N");
        FCLOSE(F1);
        EXIT(1);
    }

    CHAR A;
    WHILE ((A = GETC(F1)) != eof)
    {
        IF (ISALPHA(A))
            IF (ISLOWER(A))
                A = TOUPPER(A);
            ELSE IF (ISUPPER(A))
                A = TOLOWER(A);
        PUTC(A, F2); //FPUTC(A, F2)
    }

    FCLOSE(F1);
    FCLOSE(F2);
    PRINTF("fILE CONVERTCHAR.C IS CREATED!!!\N");

    RETURN 0;
}


포인터 기초(1)

메모리 주소 연산자와 주소 출력

//main.c
#pragma warning(disable:4996)
#include <stdio.h>

int main(void)
{
    int input;

    printf("정수 입력: ");
    scanf("%d", &input);
    printf("입력 값: %d\n", input);
    printf("주소값: %p(16진수)\n", &input);
    printf("주소값: %llu(10진수)\n", (uintptr_t)&input);

    printf("주소값 크기: %zu\n", sizeof(&input)); //%zd도 가능

    return 0;
}

정수 입력: 100 입력 값: 100 주소값: 000000C1E68FF914(16진수) 주소값: 832796883220(10진수) 주소값 크기: 8

참고로 변수를 기본값 0으로 초기화하듯이, 포인터는 기본값 NULL로 초기화한다.

int *ptr = NULL;

물론 이렇게라고 안하면 포인터가 쓰레기 주소값을 저장하게 되니까,, 좀… 그렇다ㅋㅋ(그냥 좋지는 않음ㅇㅇ)

그래서 위와 같이 해주는 건데 좀 귀찮다 싶으면 매크로 상수를 써주면 편하다.일일이 NULL로 초기화해줄 필요가 없다.

#define NULL ((void *)0)

포인터 변수와 간접연산자 *를 이용한 간접참조

//main.c
#pragma warning(disable:4996)
#include <stdio.h>

int main(void)
{
    int i = 100;
    char c = 'A';

    int* pi = &i;
    char* pc = &c;
    printf("간접참조 출력: %d %c\n", *pi, *pc);

    *pi = 200; //*pi가 가리키는 곳(i)에 200을 넣어줌
    *pc = 'B'; //*pc가 가리키는 곳(c)에 문자'B'를 넣어줌
    printf("직접참조 출력: %d %c\n", i, c);

    //만약 이렇게 되면 200은 메모리 상에 아직 남아있는지?
    //아니면 메모리가 해제되는지?
    pi = NULL;
    

    return 0;
}

위와 같이 마지막줄에 pi = NULL를 써주면 메모리가 해제되는지 궁금해서 여쭤보았다.

헷갈린게 이렇게 하면 i에 200이 들어가 있는 꼴이니 pi=NULL을 해주든 안해주든 상관없다.

포인터를 이용하여 두수의 값을 교환해보기

함수는 기본적으로 하나밖에 반환을 못해준다.

그럴때 우리는 포인터를 활용해서 여러개를 반환해주는 게 가능하다.

//main.c
#pragma warning(disable:4996)
#include <stdio.h>

void swap(int* x, int* y)
{
    int t;
    t = *x;
    *x = *y;
    *y = t;
}


int main(void)
{
    int num1, num2;
    scanf_s("%d%d", &num1, &num2);

    swap(&num1, &num2);

    printf("%d %d\n", num1, num2);

    return 0;
}

실습문제2)참조코드

참조1)

“f3.txt”파일에서 숫자를 추출하여 총합을 계산하는 C언어 코드입니다. 코드는 파일을 읽어온 후, 문자열에서 숫잘를 추출하고 총합을 계산합니다.

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

#define MAX_LEN 100

int main(void)
{
    FILE* f;
    if (fopen_s(&f, "f3.txt", "r") != 0)
    {
        printf("파일이 열리지 않습니다. \n");
        exit(1);
    }

    char inputStr[MAX_LEN];
    int numbers[MAX_LEN];
    int numbersCount = 0;

    // 파일 내용 읽기
    if (fgets(inputStr, MAX_LEN, f) == NULL)
    {
        printf("파일 내용을 읽을 수 없습니다.\n");
        fclose(f);
        exit(1);
    }

    fclose(f);

    // 숫자 추출 및 합 계산
    int sum = 0;
    const char* currentPos = inputStr;

    while (*currentPos != '\0')
    {
        if (isdigit(*currentPos))
        {
            printf("number> %d\n", atoi(currentPos));
            numbers[numbersCount++] = atoi(currentPos);
            while (isdigit(*currentPos))
                currentPos++;
        }
        else
        {
            currentPos++;
        }
    }

    // 출력
    printf("추출된 숫자 배열: ");
    for (int i = 0; i < numbersCount; i++)
    {
        printf("%d ", numbers[i]);
        sum += numbers[i];
    }
    printf("\n");

    printf("숫자의 총 합: %d\n", sum);

    return 0;
}

참조2

파일에 있는 숫자들을 일의자리씩 추출해서 모두 더하여 출력합니다.

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

#define MAX_LEN 100

int main(void)
{
    FILE* f;
    if (fopen_s(&f, "f3.txt", "r") != 0)
    {
        printf("파일이 열리지 않습니다. \n");
        exit(1);
    }

    int ch, cnt = 0;
    int sum = 0;
    int countOne = 0;

    while ((ch = fgetc(f)) != EOF)
    {
        if (isdigit(ch))
        {
            int digit = ch - '0';
            printf("\ndigit: %d ", digit);
            sum += digit;
            

            printf("\n");
        }
    }
    printf("숫자의 총 합: %d\n", sum);
    fclose(f);

    return 0;
}

최종 CI코드

#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>

#define MAX_LEN 100

int main(void)
{
    FILE* f,*file_export;
    if (fopen_s(&f, "f3.txt", "r") != 0)
    {
        printf("파일이 열리지 않습니다. \n");
        exit(1);
    }
    if ((file_export = fopen("f3out.txt", "w")) == NULL)
    {
        printf("cannot open this file\n");
        fclose(f);
        exit(1);
    }

    int ch, cnt = 0;
    int sum = 0;
    int countOne = 0;

    while ((ch = fgetc(f)) != EOF)
    {
        if (isdigit(ch))
        {
            int digit = ch - '0';
            sum += digit;
            
            putc(ch, file_export);
            
        }
    }

    printf("%d\n", sum);

    fclose(f);
    fclose(file_export);
    
    return 0;
}

댓글남기기