안녕하세요,
81.7 심사문제 풀고있는데 81.3의 전체코드에서 파일이름을 sample.bmp로 바꾸고, ascii 문자열을 번호로 바꾸는거 외에 다르게 바꿔야할 점이 뭐가 있나요??
고민해봐도 통과가 안되어서요ㅠㅠ
감사합니다!
sample50x50.bmp 이미지를 sample.bmp로 바꿔서 실행해보고 결과가 문제에서 제시된 결과와 일치하는지 확인해보세요.
https://text-compare.com/
텍스트 비교 사이트에서 문제에서 제시한 결과가 내가 코드로 얻은 결과를 비교하면 일치 여부를 판단하기 쉽습니다.
소스 코드를 첨부해서 질문을 올려도 됩니다.
축하합니다!
이 정도 문제까지 푸는 사람은 많지 않아요.
그러나 현업에서는 바이너리 파일을 다루는 일도 많죠. 간접 체험할 수 있는 좋은 문제입니다.
81예제에서
char ascii[] = { '9', '8', '7', '6', '5', '4', '3', '2', '1', '0', ' ' };
fpBmp = fopen("sample.bmp", "rb");
printf(fpTxt, "%c%c", c, c); // 텍스트 파일에 문자 출력
printf(fpTxt, "\n");
이 4줄 바꿨는데 어디를 더 수정해야할지 모르겠습니다ㅠㅠ
심사문제의 예시와 같은 결과는 나오나요?
파일로 작성한 것으로 보이는데, 먼저 원하는 결과가 나오는지 확인하세요.
심사문제는 파일로 테스트할 수 없어서 화면에 출력해야 합니다.
위에는 printf 문법이 아니라 sprintf 문법 같은데요.
sprintf를 printf로 바꾼 것이면 필요한 함수 인자가 다르니까 안 됩니다.
fptxt는 있을 필요가 없고, printf로 화면에 문자를 출력하게 하면 됩니다.
"%c%c"처럼 왜 같은 문자를 두 번씩 출력하는지요?
파일로 작성해도 심사문제가 요구하는 것과 그림의 모양이 다를 것 같습니다.
포럼 상단에 81.7로 검색하면 이전 질문과 답변에서 다양한 힌트를 발견할 수 있을 겁니다. 참고하세요.
#define _CRT_SECURE_NO_WARNINGS // fopen 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h> // fopen, fread, fseek, fprintf, fclose 함수가 선언된 헤더 파일
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
#pragma pack(push, 1) // 구조체를 1바이트 크기로 정렬
typedef struct _BITMAPFILEHEADER // BMP 비트맵 파일 헤더 구조체
{
unsigned short bfType; // BMP 파일 매직 넘버
unsigned int bfSize; // 파일 크기
unsigned short bfReserved1; // 예약
unsigned short bfReserved2; // 예약
unsigned int bfOffBits; // 비트맵 데이터의 시작 위치
} BITMAPFILEHEADER;
typedef struct _BITMAPINFOHEADER // BMP 비트맵 정보 헤더 구조체(DIB 헤더)
{
unsigned int biSize; // 현재 구조체의 크기
int biWidth; // 비트맵 이미지의 가로 크기
int biHeight; // 비트맵 이미지의 세로 크기
unsigned short biPlanes; // 사용하는 색상판의 수
unsigned short biBitCount; // 픽셀 하나를 표현하는 비트 수
unsigned int biCompression; // 압축 방식
unsigned int biSizeImage; // 비트맵 이미지의 픽셀 데이터 크기
int biXPelsPerMeter; // 그림의 가로 해상도(미터당 픽셀)
int biYPelsPerMeter; // 그림의 세로 해상도(미터당 픽셀)
unsigned int biClrUsed; // 색상 테이블에서 실제 사용되는 색상 수
unsigned int biClrImportant; // 비트맵을 표현하기 위해 필요한 색상 인덱스 수
} BITMAPINFOHEADER;
typedef struct _RGBTRIPLE // 24비트 비트맵 이미지의 픽셀 구조체
{
unsigned char rgbtBlue; // 파랑
unsigned char rgbtGreen; // 초록
unsigned char rgbtRed; // 빨강
} RGBTRIPLE;
#pragma pack(pop)
#define PIXEL_SIZE 3 // 픽셀 한 개의 크기 3바이트(24비트)
#define PIXEL_ALIGN 4 // 픽셀 데이터 가로 한 줄은 4의 배수 크기로 저장됨
int main()
{
FILE *fpBmp; // 비트맵 파일 포인터
FILE *fpTxt; // 텍스트 파일 포인터
BITMAPFILEHEADER fileHeader; // 비트맵 파일 헤더 구조체 변수
BITMAPINFOHEADER infoHeader; // 비트맵 정보 헤더 구조체 변수
unsigned char *image; // 픽셀 데이터 포인터
int size; // 픽셀 데이터 크기
int width, height; // 비트맵 이미지의 가로, 세로 크기
int padding; // 픽셀 데이터의 가로 크기가 4의 배수가 아닐 때 남는 공간의 크기
// 각 픽셀을 표현할 ASCII 문자. 인덱스가 높을 수록 밝아지는 것을 표현
char ascii[] = { '9', '8', '7', '6', '5', '4', '3', '2', '1', '0', ' ' }; // 11개
fpBmp = fopen("sample.bmp", "rb"); // 비트맵 파일을 바이너리 모드로 열기
if (fpBmp == NULL) // 파일 열기에 실패하면
return 0; // 프로그램 종료
// 비트맵 파일 헤더 읽기. 읽기에 실패하면 파일 포인터를 닫고 프로그램 종료
if (fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, fpBmp) < 1)
{
fclose(fpBmp);
return 0;
}
// 매직 넘버가 MB가 맞는지 확인(2바이트 크기의 BM을 리틀 엔디언으로 읽었으므로 MB가 됨)
// 매직 넘버가 맞지 않으면 프로그램 종료
if (fileHeader.bfType != 'MB')
{
fclose(fpBmp);
return 0;
}
// 비트맵 정보 헤더 읽기. 읽기에 실패하면 파일 포인터를 닫고 프로그램 종료
if (fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, fpBmp) < 1)
{
fclose(fpBmp);
return 0;
}
// 24비트 비트맵이 아니면 프로그램 종료
if (infoHeader.biBitCount != 24)
{
fclose(fpBmp);
return 0;
}
size = infoHeader.biSizeImage; // 픽셀 데이터 크기
width = infoHeader.biWidth; // 비트맵 이미지의 가로 크기
height = infoHeader.biHeight; // 비트맵 이미지의 세로 크기
// 이미지의 가로 크기에 픽셀 크기를 곱하여 가로 한 줄의 크기를 구하고 4로 나머지를 구함
// 그리고 4에서 나머지를 빼주면 남는 공간을 구할 수 있음.
// 만약 남는 공간이 0이라면 최종 결과가 4가 되므로 여기서 다시 4로 나머지를 구함
padding = (PIXEL_ALIGN - ((width * PIXEL_SIZE) % PIXEL_ALIGN)) % PIXEL_ALIGN;
if (size == 0) // 픽셀 데이터 크기가 0이라면
{
// 이미지의 가로 크기 * 픽셀 크기에 남는 공간을 더해주면 완전한 가로 한 줄 크기가 나옴
// 여기에 이미지의 세로 크기를 곱해주면 픽셀 데이터의 크기를 구할 수 있음
size = (width * PIXEL_SIZE + padding) * height;
}
image = malloc(size); // 픽셀 데이터의 크기만큼 동적 메모리 할당
// 파일 포인터를 픽셀 데이터의 시작 위치로 이동
fseek(fpBmp, fileHeader.bfOffBits, SEEK_SET);
// 파일에서 픽셀 데이터 크기만큼 읽음. 읽기에 실패하면 파일 포인터를 닫고 프로그램 종료
if (fread(image, size, 1, fpBmp) < 1)
{
fclose(fpBmp);
return 0;
}
fclose(fpBmp); // 비트맵 파일 닫기
fpTxt = fopen("ascii.txt", "r"); // 결과 출력용 텍스트 파일 열기
if (fpTxt == NULL) // 파일 열기에 실패하면
{
free(image); // 픽셀 데이터를 저장한 동적 메모리 해제
return 0; // 프로그램 종료
}
// 픽셀 데이터는 아래 위가 뒤집혀서 저장되므로 아래쪽부터 반복
// 세로 크기만큼 반복
for (int y = height - 1; y >= 0; y--)
{
// 가로 크기만큼 반복
for (int x = 0; x < width; x++)
{
// 일렬로 된 배열에 접근하기 위해 인덱스를 계산
// (x * 픽셀 크기)는 픽셀의 가로 위치
// (y * (세로 크기 * 픽셀 크기))는 픽셀이 몇 번째 줄인지 계산
// 남는 공간 * y는 줄별로 누적된 남는 공간
int index = (x * PIXEL_SIZE) + (y * (width * PIXEL_SIZE)) + (padding * y);
// 현재 픽셀의 주소를 RGBTRIPLE 포인터로 변환하여 RGBTRIPLE 포인터에 저장
RGBTRIPLE *pixel = (RGBTRIPLE *)&image[index];
// RGBTRIPLE 구조체로 파랑, 초록, 빨강값을 가져옴
unsigned char blue = pixel->rgbtBlue;
unsigned char green = pixel->rgbtGreen;
unsigned char red = pixel->rgbtRed;
// 파랑, 초록, 빨강값의 평균을 구하면 흑백 이미지를 얻을 수 있음
unsigned char gray = (red + green + blue) / PIXEL_SIZE;
// 흑백값에 ASCII 문자의 개수를 곱한 뒤 256으로 나누면 흑백값에 따라
// ASCII 문자의 인덱스를 얻을 수 있음
char c = ascii[gray * sizeof(ascii) / 256];
// 비트맵 이미지에서는 픽셀의 가로, 세로 크기가 똑같지만
// 보통 ASCII 문자는 세로로 길쭉한 형태이므로 정사각형 모양과 비슷하게 보여주기 위해
// 같은 문자를 두 번 저장해줌
printf("%c", c); // 텍스트 파일에 문자 출력
}
printf("\n"); // 가로 픽셀 저장이 끝났으면 줄바꿈을 해줌
}
fclose(fpTxt); // 텍스트 파일 닫기
free(image); // 픽셀 데이터를 저장한 동적 메모리 해제
return 0;
}
이렇게 수정했는데 어느부분을 더 수정해야할까요?ㅠㅠ
fpTxt = fopen("ascii.txt", "r"); // 결과 출력용 텍스트 파일 열기
심사문제는 출력된 텍스트 일치로만 판단합니다.
따라서 ascii.txt 파일을 생성하는 과정이 필요하지 않습니다.
그럼에도 문의한 코드를 실행했을 때 ascii.txt가 생성되어야 하지만, 생성되지 않습니다.
fopen으로 ascii.txt를 열었다면 결과를 저장하기 위한 용도일 것인데
모드는 "r"로 되어 있어서 읽기 모드입니다. 잉? 아아앗?
그래서 화면에 출력되는 결과도 없고, ascii.txt 파일도 생성되지 않습니다.
따라서 결과가 어떤 과정을 거치는지 전혀 알 수 없습니다.
UNIT 81.6을 실행해보세요.
해당 예제를 완벽하게 이해하면 81.7은 쉽습니다.
UNIT 81.6의 예제에서는
fpTxt = fopen("ascii.txt","w");
ascii.txt 파일을 생성해서 결과를 보는 것이 목적이라면 모드가 "w"이고, 쓰기 모드로 확실하게 되어 있습니다.
UNIT 81.6에서는
UNIT 81.7에서는 숫자 형태로 바꿀 뿐입니다. UNIT 81.6에서는 ascii.txt 파일에 쓰기를 하기 위해 fprintf를 사용했습니다. fprintf의 f는 file을 뜻합니다. 파일에 쓰는 printf라는 뜻이 fprintf입니다. UNIT 81.7에서는 파일에 쓰지 않고 화면에 출력하는 문제입니다. 따라서 ascii.txt 파일을 생성할 필요도 읽을 필요도 없습니다. 화면에 출력하면 됩니다. printf만 쓰면 된다는 뜻이죠. 변환할 때 기호 대신에 숫자로 바꾼다 파일에 쓰지 않고 화면에 출력한다 그렇다면 UNIT 81.6의 예제를 토대로 소스 코드에 있는 sample50x50.bmp를 sample.bmp로 바꿔서 테스트해본다. UNIT 81.6 예제에서 기호를 숫자로 바꿔서 잘 동작하는지 확인한다. ascii.txt에 숫자로 잘 생성되는지 확인한다. 파일로 잘 동작하는 것을 확인했다면 파일 대신 화면에 출력하는 버전으로 바꿔 본다. 이렇게 차례대로 하나씩 단계를 거치면서 디벨롭해가면 될 겁니다. char
ascii[] = { '#', '#', '@', '%', '=', '+', '*', ':', '-', '.', ' ' };