바이너리 파일을 읽습니다. 사용자 정의 데이터 유형을 바이너리 파일에 쓰고 읽습니다. 표준 데이터 유형을 바이너리 파일에 쓰기

파일. 더욱이 하드웨어 수준의 기술 구현 관점에서 볼 때 텍스트 파일은 바이너리 파일의 특수한 경우이므로 넓은 의미에서 모든 파일은 "바이너리 파일"의 정의에 맞습니다.

일반적으로 이 용어는 바이너리 파일 소비자와 파일 자체 간의 관계를 측정한 것입니다. 소비자가 특정 파일을 더 높은 수준의 파일로 변환할 수 있는 구조와 규칙을 알고 있다면 그 파일은 바이너리가 아닙니다. 예를 들어 실행 파일은 컴퓨터 사용자에게는 바이너리이지만 운영 체제에서는 바이너리가 아닙니다. [ ]

심상

바이너리 파일을 시각적으로 표현하기 위해 파일은 동일한 크기의 조각으로 나누어 숫자로 표시되며 일반적으로 16진수, 때로는 8진수, 2진수 또는 10진수로 작성됩니다. 표시된 청크 크기는 1옥텟뿐만 아니라 2옥텟 또는 4옥텟과 동일할 수 있습니다(여러 옥텟의 청크로 분할하는 경우 사용되는 플랫폼에서 채택한 바이트 순서가 사용됩니다). 조각 크기에 대한 표현된 숫자 범위의 의존성은 표에 나와 있습니다.

옥텟 비트 수 16진수 8진수 소수
서명되지 않은
소수
상의
1 8 00

FF
000

377
0

255
-128

127
2 16 0000

FFFF
000000

177777
0

65535
-32768

32767
4 32 00000000

FFFFFFFF
00000000000

37777777777
0

4294967295
-2147483648

2147483647

종종 숫자 바이트 값 외에도 ASCII와 같은 코드 페이지 문자도 출력됩니다. 다음 예는 소위를 보여줍니다. 클래식 덤프 Wikipedia 로고 PNG 파일의 시작 부분(한 줄에 16바이트의 옥텟별 16진수 표현, 오른쪽에 인쇄 가능한 ASCII 문자 포함):

00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR| 00000010 00 00 00 87 00 00 00 a0 08 03 00 00 00 11 90 8f |.............| 00000020 b6 00 00 00 04 67 41 4d 41 00 00 d6 d8 d4 4f 58 |.....gAMA.....OX| 00000030 32 00 00 00 19 74 45 58 74 53 6f 66 74 77 61 72 |2....tEXt소프트웨어| 00000040 65 00 41 64 6f 62 65 20 49 6d 61 67 65 52 65 61 |e.Adobe ImageRea| 00000050 64 79 71 c9 65 3c 00 00 03 00 50 4c 54 45 22 22 |dyq.e<....PLTE""| 00000060 22 56 56 56 47 47 47 33 33 33 30 30 30 42 42 42 |"VVVGGG333000BBB| 00000070 4b 4b 4b 40 40 40 15 15 15 4f 4f 4f 2c 2c 2c 3c |KKK@@@...OOO,<| 00000080 3c 3c 3e 3e 3e 3a 39 39 04 04 04 1d 1d 1d 35 35 |<<>>>:99......55| 00000090 35 51 50 50 37 37 37 11 11 11 25 25 25 0d 0d 0d |5QPP777...%%%...| 000000a0 27 27 27 1a 1a 1a 38 38 38 2a 2a 2a 08 08 08 20 |"""...888**... | 000000b0 20 20 17 17 17 2e 2e 2e 13 13 13 bb bb bb 88 88 | . ............|

도구

시각화를 위해

  • 디버그(Microsoft Windows에서는 부분적으로)
  • hexdump(FreeBSD, GNU/Linux 등)

편집용

  • HEX 편집기
    • 안녕하세요(모든 운영 체제용, 무료 소프트웨어)
    • 안녕(DOS, Microsoft Windows, Windows NT용)
    • WinHex(윈도우용)

태그: 바이너리 파일, fseek, ftell, fpos, fread, fwrite

바이너리 파일

텍스트 파일은 데이터를 텍스트 형식으로 저장합니다(sic!). 이는 예를 들어 정수 12345678을 파일에 쓰면 숫자가 정수 유형으로 배치되어 있음에도 불구하고 8문자, 즉 8바이트의 데이터가 기록된다는 의미입니다. 또한 데이터의 출력 및 입력 형식이 지정됩니다. 즉, 파일에서 숫자를 읽거나 파일에 쓸 때마다 숫자가 문자열로 변환되거나 그 반대로 변환됩니다. 이는 피할 수 있는 비용이 많이 드는 작업입니다.

텍스트 파일을 사용하면 사람이 이해할 수 있는 형식으로 정보를 저장할 수 있습니다. 그러나 데이터를 바이너리 형식으로 직접 저장하는 것은 가능합니다. 이러한 목적으로 바이너리 파일이 사용됩니다.

#포함하다 #포함하다 #포함하다 #define ERROR_FILE_OPEN -3 void main() ( FILE *output = NULL; int 숫자; 출력 = fopen("D:/c/output.bin", "wb"); if (output == NULL) ( printf(" 파일 열기 오류"); getch(); exit(ERROR_FILE_OPEN); ) scanf("%d", &number); fwrite(&number, sizeof(int), 1, 출력); fclose(output); _getch(); )

프로그램을 실행하고 output.bin 파일의 내용을 살펴보세요. 사용자가 입력한 숫자는 바이너리 형식으로 파일에 직접 기록됩니다. 16진수 표현을 지원하는 편집기(Total Commander, Far)에서 파일을 열고 이를 확인할 수 있습니다.

파일 쓰기는 함수를 사용하여 수행됩니다.

Size_t fwrite (const void * ptr, size_t 크기, size_t 개수, FILE * 스트림);

이 함수는 성공적으로 작성된 요소의 수를 반환합니다. 배열에 대한 포인터, 한 요소의 크기, 요소 수 및 파일 스트림에 대한 포인터를 인수로 사용합니다. 물론 배열 대신 모든 객체를 전달할 수 있습니다.

바이너리 파일에 객체를 쓰는 것은 객체를 표시하는 것과 유사합니다. 데이터는 RAM에서 가져와 있는 그대로 기록됩니다. fread 함수는 읽기에 사용됩니다.

Size_t fread(void * ptr, size_t 크기, size_t 개수, FILE * 스트림);

이 함수는 ptr에 배치된 성공적으로 읽은 요소의 수를 반환합니다. 전체적으로 size 바이트의 count 요소를 읽습니다. 이제 변수에 숫자를 다시 세어 보겠습니다.

#포함하다 #포함하다 #포함하다 #define ERROR_FILE_OPEN -3 void main() ( FILE *input = NULL; int number; input = fopen("D:/c/output.bin", "rb"); if (input == NULL) ( printf(" 파일 열기 오류"); getch(); exit(ERROR_FILE_OPEN); ) fread(&number, sizeof(int), 1, input); printf("%d", number); fclose(input); _getch(); )

fseek

바이너리 파일 작업에 있어 중요한 기능 중 하나는 fseek 기능입니다.

Int fseek(FILE * 스트림, long int 오프셋, int 원점);

이 함수는 스트림과 관련된 위치 포인터를 새 위치로 설정합니다. 위치 표시기는 파일에서 우리가 멈춘 위치를 나타냅니다. 파일을 열 때 위치는 0입니다. 데이터의 바이트가 기록될 때마다 위치 포인터가 1씩 앞으로 이동합니다.
fseek는 스트림에 대한 포인터와 원점을 기준으로 오프셋 바이트의 이동을 인수로 사용합니다. 원점은 세 가지 값을 가질 수 있습니다

  • SEEK_SET- 파일의 시작
  • SEEK_CUR- 현재 파일 위치
  • SEEK_END- 파일 끝. 불행하게도 표준에서는 파일 끝이 무엇인지 정의하지 않으므로 이 함수를 신뢰할 수 없습니다.

성공하면 함수는 0을 반환합니다.

이전 예에 추가해 보겠습니다. 숫자를 기록한 다음 포인터를 파일 시작 부분으로 이동하여 읽습니다.

#포함하다 #포함하다 #포함하다 #define ERROR_FILE_OPEN -3 void main() ( FILE *iofile = NULL; int 숫자; iofile = fopen("D:/c/output.bin", "w+b"); if (iofile == NULL) ( printf ("파일 열기 오류"); scanf("%d", &number); fread(&number, sizeof(int), iofile);

대신 위치 표시기를 처음으로 이동시키는 되감기 기능을 사용할 수도 있습니다.

C는 파일에 위치 표시기의 위치를 ​​저장하는 데 사용되는 특수 유형 fpos_t를 정의합니다.
기능

Int fgetpos(FILE * 스트림, fpos_t * pos);

pos 변수를 현재 위치에 할당하는 데 사용됩니다. 기능

Int fsetpos(FILE * 스트림, const fpos_t * pos);

pos 변수에 저장된 위치로 포인터를 이동하는 데 사용됩니다. 성공하면 두 함수 모두 0을 반환합니다.

Long int ftell(FILE * 스트림);

파일의 시작 부분을 기준으로 표시기의 현재 위치를 반환합니다. 바이너리 파일의 경우 이는 바이트 수이고 텍스트 파일의 경우 정의되지 않습니다(텍스트 파일이 단일 바이트 문자로 구성된 경우 바이트 수이기도 함).

예를 살펴보겠습니다. 사용자가 숫자를 입력합니다. 파일의 처음 4바이트: 입력된 숫자 수를 나타내는 정수입니다. 사용자가 숫자 입력을 중단한 후 파일의 시작 부분으로 이동하여 거기에 입력된 요소 수를 씁니다.

#포함하다 #포함하다 #포함하다 #define ERROR_OPEN_FILE -3 void main() ( FILE *iofile = NULL; unsigned counter = 0; int num; int yn; iofile = fopen("D:/c/numbers.bin", "w+b"); if (iofile == NULL) ( printf("파일 열기 오류"); getch();exit(ERROR_OPEN_FILE); ) fwrite(&counter, sizeof(int), 1, iofile) do ( printf("새 번호를 입력하시겠습니까?" ); scanf("%d", &yn); if (yn == 1) ( scanf("%d", &num); fwrite(&num, sizeof(int), 1, iofile); counter++; ) else ( 되감기 (iofile); fwrite(&counter, sizeof(int), 1, iofile);

두 번째 프로그램은 먼저 쓰여진 숫자의 개수를 읽은 다음, 숫자를 순서대로 읽고 표시합니다.

#포함하다 #포함하다 #포함하다 #define ERROR_OPEN_FILE -3 void main() ( FILE *iofile = NULL; 부호 없는 카운터; int i, num; iofile = fopen("D:/c/numbers.bin", "rb"); if (iofile == NULL ) ( printf("파일 열기 오류"); getch(); exit(ERROR_OPEN_FILE); ) fread(&counter, sizeof(int), 1, iofile);< counter; i++) { fread(&num, sizeof(int), 1, iofile); printf("%d\n", num); } fclose(iofile); getch(); }

1. 10*sizeof(int)바이트 크기의 바이너리 파일이 있습니다. 사용자가 셀 번호를 입력하면 해당 번호가 기록됩니다. 각 작업 후에는 모든 숫자가 표시됩니다. 먼저 읽기-쓰기 모드로 파일을 열려고 합니다. 이것이 실패하면 파일 생성을 시도하고, 파일 생성에 성공하면 읽기 및 쓰기를 위해 파일을 열려는 시도를 반복합니다.

#포함하다 #포함하다 #포함하다 #define SIZE 10 void main() ( const char filename = "D:/c/state"; FILE *bfile = NULL; int pos; int value = 0; int i; char wasCreated; do ( wasCreated = 0; bfile = fopen(filename, "r+b"); if (NULL == bfile) ( printf("파일을 생성해 보십시오...\n"); getch(); bfile = fopen(filename, "wb"); if (bfile == NULL) ( printf("파일 생성 시 오류 발생"); getch();exit(1); ) for (i = 0; i< SIZE; i++) { fwrite(&value, sizeof(int), 1, bfile); } printf("File created successfully...\n"); fclose(bfile); wasCreated = 1; } } while(wasCreated); do { printf("Enter position "); scanf("%d", &pos); if (pos < 0 || pos >= SIZE) ( break; ) printf("값을 입력하세요 "); scanf("%d", &value); fseek(bfile, pos*sizeof(int), SEEK_SET); fwrite(&value, sizeof(int), 1, bfile); rewind(bfile); for (i = 0; i !}< SIZE; i++) { fread(&value, sizeof(int), 1, bfile); printf("%d ", value); } printf("\n"); } while(1); fclose(bfile); }

2. 바이너리 파일에 단어를 씁니다. 형식은 다음과 같습니다. 먼저 문자 수, 그 다음에는 널 문자가 없는 단어 자체입니다. 단어 길이가 0이면 더 이상 단어가 없습니다. 먼저 사용자에게 단어를 물어본 다음 다시 읽어줍니다.

#포함하다 #포함하다 #포함하다 #포함하다 #define ERROR_FILE_OPEN -3 void main() ( const char filename = "C:/c/words.bin"; const char termWord = "exit"; char buffer; unsigned int len; FILE *wordsFile = NULL; printf("열기" file...\n"); wordFile = fopen(filename, "w+b"); if (wordsFile == NULL) ( printf("파일 열기 오류"); getch();exit(ERROR_FILE_OPEN); ) printf ("단어 입력\n"); do ( scanf("%127s", buffer); if (strcmp(buffer, termWord) == 0) ( len = 0; fwrite(&len, sizeof(unsigned), 1,wordsFile ); break; len = strlen(buffer); fwrite(&len, sizeof(unsigned), 1,wordsFile); rewind(wordsFile); getch(); do ( fread(&len, sizeof(int), 1, wordFile); if (len == 0) ( break; ) fread(buffer, 1, len, wordFile ); printf("%s\n", buffer); ;

3. 작업은 텍스트 파일에서 데이터를 읽고 이진 파일에 쓰는 것입니다. 이 문제를 해결하기 위해 래퍼 함수를 ​​만들어 보겠습니다. 파일 이름, 액세스 모드, 파일이 성공적으로 열린 경우 실행해야 하는 함수 및 이 함수에 대한 인수를 사용합니다. 인수가 많을 수 있고 유형이 다를 수 있으므로 구조에 대한 포인터로 전달될 수 있습니다. 함수가 실행된 후 파일이 닫힙니다. 이렇게 하면 리소스 확보에 대해 생각할 필요가 없습니다.

#포함하다 #포함하다 #포함하다 #define DEBUG #ifdef DEBUG #define debug(data) printf("%s", data); #else #define 디버그(데이터) #endif const char inputFile = "D:/c/xinput.txt"; const char outputFile = "D:/c/output.bin"; struct someArgs ( int* 항목; size_t 숫자; ); int writeToFile(FILE *file, void* args) ( size_t i; struct someArgs *data = (struct someArgs*) args; debug("파일에 쓰기\n") fwrite(data->items, sizeof(int), data ->번호, 파일); debug("쓰기 완료\n") return 0; ) int readAndCallback(FILE *file, void* args) ( struct someArgs data; size_t size, i = 0; int result; debug("read 파일에서\n") fscanf(file, "%d", &size); data.items = (int*) malloc(size*sizeof(int)); data.number = size; while (!feof(file)) ( fscanf(file, "%d", &data.items[i]); i++; ) debug("OpenFile로 호출\n") result = withOpenFile(outputFile, "w", writeToFile, &data debug("읽기 완료); \n") free(data.items); return result; ) int doStuff() ( return withOpenFile(inputFile, "r", readAndCallback, NULL); ) //Wrapper - 함수가 파일을 엽니다. 파일이 성공적으로 열리면 //fun 함수가 호출됩니다. 인수는 매우 다를 수 있으므로 //void* 포인터를 통해 전달됩니다. 인수 유형으로 //int withOpenFile(const char *filename, const char *mode, int (*fun)(FILE* source, void* args), void* args) 구조를 사용하는 것이 합리적입니다 ( FILE *file = fopen(파일 이름, 모드); int err; debug("파일을 열려고 시도합니다") debug(파일 이름) debug("\n") if (file != NULL) ( err = fun(file, args); ) else ( return 1; ) debug("파일 닫기") debug(filename) debug("\n") fclose(file) return void main() ( printf("result = %d", doStuff()); getch ();

4. saveInt32Array 함수를 사용하면 int32_t 유형의 배열을 파일에 저장할 수 있습니다. 그 반대인 loadInt32Array는 배열을 다시 읽습니다. loadInt32Array 함수는 먼저 전달된 배열을 초기화하므로 포인터에 포인터를 전달해야 합니다. 또한 전달된 크기 매개변수에 읽기 배열 크기를 쓰기 때문에 포인터로 전달됩니다.

#포함하다 #포함하다 #포함하다 #포함하다 #define SIZE 100 int saveInt32Array(const char *filename, const int32_t *a, size_t size) ( FILE *out = fopen(filename, "wb"); if (!out) ( return 0; ) // 배열 fwrite (&size, sizeof(size_t), 1, out); //전체 배열 쓰기 fwrite(a, sizeof(int32_t), size, out) return 1; *a, size_t *size) ( FILE *in = fopen(filename, "rb"); if (!in) ( return 0; ) //배열의 길이를 읽습니다. fread(size, sizeof(size_t), 1, in) ; //배열 초기화 (*a) = (int32_t*) malloc(sizeof(int32_t) * (*size)); if (!(*a)) ( return 0; ) // 전체 배열 읽기 ((*a ), sizeof(int32_t), *size, in); return 1; ) void main() ( const char *tmpFilename = "tmp.bin"; int32_t exOut; int32_t *exIn = NULL; size_t realSize; int 나는 (나는 = 0; 나는< SIZE; i++) { exOut[i] = i*i; } saveInt32Array(tmpFilename, exOut, SIZE); loadInt32Array(tmpFilename, &exIn, &realSize); for (i = 0; i < realSize; i++) { printf("%d ", exIn[i]); } _getch(); }

5. 조회 테이블을 생성합니다. 프로그램 속도를 높이려면 함수를 계산하는 대신 먼저 특정 간격으로 함수 값을 특정 정확도로 계산한 다음 표에서 값을 가져올 수 있습니다. 프로그램은 먼저 주어진 매개변수로 함수를 표로 작성하고 이를 파일에 저장한 다음 이미 값을 결정하는 데 사용된 미리 계산된 배열을 로드합니다. 이 프로그램에서 모든 함수는 오류 번호를 저장하는 Result 유형의 변수를 반환합니다. 함수가 문제 없이 작동하면 Ok(0)를 반환합니다.

#define _CRT_SECURE_NO_WARNINGS //예, 이제 추가해야 합니다. 그렇지 않으면 #include가 작동하지 않습니다. #포함하다 #포함하다 #포함하다 #포함하다 //각 함수는 결과를 반환합니다. Ok와 같으면 함수는 //문제 없이 작동한 것입니다. typedef int Result; //가능한 작업 결과 #define Ok 0 #define ERROR_OPENING_FILE 1 #define ERROR_OUT_OF_MEMORY 2 //표로 만들 함수 double mySinus(double x) ( return sin(x); ) 결과 tabFunction(const char *filename, double from , double to, double step, double (*f)(double)) ( 결과 r; FILE *out = fopen(filename, "wb"); double value; if (!out) ( r = ERROR_OPENING_FILE; goto EXIT; ) fwrite (&from, sizeof(from), 1, out); fwrite(&to, sizeof(to), 1, out) fwrite(&step, sizeof(step), 1, out);< to; from += step) { value = f(from); fwrite(&value, sizeof(double), 1, out); } r = Ok; EXIT: fclose(out); return r; } Result loadFunction(const char *filename, double **a, double *from, double *to, double *step) { Result r; uintptr_t size; FILE *in = fopen(filename, "rb"); if (!in) { r = ERROR_OPENING_FILE; goto EXIT; } //Считываем вспомогательную информацию fread(from, sizeof(*from), 1, in); fread(to, sizeof(*to), 1, in); fread(step, sizeof(*step), 1, in); //Инициализируем массив size = (uintptr_t) ((*to - *from) / *step); (*a) = (double*) malloc(sizeof(double)* size); if (!(*a)) { r = ERROR_OUT_OF_MEMORY; goto EXIT; } //Считываем весь массив fread((*a), sizeof(double), size, in); r = Ok; EXIT: fclose(in); return r; } void main() { const char *tmpFilename = "tmp.bin"; Result r; double *exIn = NULL; int accuracy, option; double from, to, step, arg; uintptr_t index; //Запрашиваем параметры для создания таблицы поиска printf("Enter parameters\nfrom = "); scanf("%lf", &from); printf("to = "); scanf("%lf", &to); printf("step = "); scanf("%lf", &step); r = tabFunction(tmpFilename, from, to, step, mySinus); if (r != Ok) { goto CATCH_SAVE_FUNCTION; } //Обратите внимание на формат вывода. Точность определяется //во время работы программы. Формат * подставит значение точности, //взяв его из списка аргументов accuracy = (int) (-log10(step)); printf("function tabulated from %.*lf to %.*lf with accuracy %.*lf\n", accuracy, from, accuracy, to, accuracy, step); r = loadFunction(tmpFilename, &exIn, &from, &to, &step); if (r != Ok) { goto CATCH_LOAD_FUNCTION; } accuracy = (int)(-log10(step)); do { printf("1 to enter values, 0 to exit: "); scanf("%d", &option); if (option == 0) { break; } else if (option != 1) { continue; } printf("Enter value from %.*lf to %.*lf: ", accuracy, from, accuracy, to); scanf("%lf", &arg); if (arg < from || arg >to) ( printf("잘못된 값\n"); 계속; ) index = (uintptr_t) ((arg - from) / step); printf("저장된 %.*lf\n계산된 %.*lf\n", 정확도, exIn, 정확도, mySinus(arg)); ) 동안 (1); r = 알았어; 종료로 이동; CATCH_SAVE_FUNCTION: ( printf("값을 저장하는 중 오류 발생"); goto EXIT; ) CATCH_LOAD_FUNCTION: ( printf("값을 로드하는 중 오류 발생"); goto EXIT; ) EXIT: free(exIn); _getch(); 종료(r); )

6. 두 가지 구조가 있습니다. 첫 번째 PersonKey는 로그인, 비밀번호, 사용자 ID 및 오프셋 필드를 저장합니다. 두 번째 PersonInfo 구조는 사용자의 성과 이름, 나이를 저장합니다. 첫 번째 구조는keys.bin 바이너리 파일에 기록되고 두 번째 구조는values.bin바이너리 파일에 기록됩니다. 오프셋 필드는 두 번째 파일에서 해당 사용자 정보의 위치를 ​​지정합니다. 따라서 첫 번째 파일에서 PersonKey를 받은 후 오프셋 필드를 사용하여 두 번째 파일에서 이 키와 관련된 정보를 추출할 수 있습니다.

왜 이런 일을 하는가? 이는 PersonInfo 구조가 큰 경우 유용합니다. 파일에서 작은 구조의 배열을 검색하는 것은 비용이 많이 들지 않으며, 큰 구조가 필요한 경우 파일에 이미 알려진 주소에서 검색할 수 있습니다.

#define _CRT_SECURE_NO_WARNINGS #include #포함하다 #포함하다 #포함하다 typedef struct PersonKey ( long long id; char 로그인; char 비밀번호; long 오프셋;//해당 PersonInfo 값의 위치) PersonKey; typedef struct PersonInfo (부호 없는 나이; char firstName; char lastName; ) PersonInfo; /* 함수는 사용자에게 데이터를 요청하고 이를 두 개의 파일에 연속으로 씁니다. */ void createOnePerson(FILE *keys, FILE *values) ( static long long id = 0; PersonKey pkey; PersonInfo pinfo; pkey.id = id++ ; //모든 값이 차례로 기록되므로 //두 번째 파일에 있는 포인터의 현재 위치는 새 항목의 위치가 됩니다. pkey.offset = ftell(values); "); scanf("%63s", pkey.login) ; printf("비밀번호: "); scanf("%63s", pkey.password); printf("연령: "); scanf("%d", &(pinfo.age)); "); scanf("%63s", pinfo.firstName); printf("성: "); scanf("%127s", pinfo.lastName); fwrite(&pkey, sizeof( pkey), 1, 키); fwrite(&pinfo, sizeof(pinfo), 1, 값 ​​) void createPersons(FILE *keys, FILE *values) ( char 버퍼; int 반복 = 1; int 카운터 = 0;// 파일의 요소 수 //요소 수를 기록하는 공간 예약 fwrite(&counter, sizeof(counter), 1, key) printf("CREATE PERSONS\n") do ( createOnePerson(keys, value); printf ("\n또 하나 더요? "); scanf("%1s", buffer); counter++; if (buffer != "y" && buffer != "Y") (peat = 0; ) ) while(repeat); //처음으로 돌아갑니다. 생성된 요소 수를 기록합니다. rewind(keys); fwrite(&counter, sizeof(counter), 1,keys) /* 키 배열 생성 */ PersonKey* readKeys(FILE *keys, int *size) ( int i; PersonKey *out = NULL; fread(size, sizeof(*size), 1, 키); out = (PersonKey*) malloc(*size * sizeof(PersonKey)); ) /* 이 함수는 작업을 단순화하기 위해 파일 배열을 반환합니다. *)malloc(sizeof(FILE*)*2); if (!files) ( return NULL; ) files = fopen(valuesFilename, "w+b"); (!files) ( fclose(files); return NULL; ) return files ) /* 키와 정보 */ void printKey(PersonKey pk) ( printf("%d. %s [%s]\n", (int)pk.id, pk.login, pk.password); ) void printInfo(PersonInfo info) ( printf("%d %s %s\n", info.age , info.firstName, info.lastName); ) /* 키별(또는 오프셋 필드별) 함수는 두 번째 파일에서 필요한 값을 검색합니다. */ PersonInfo readInfoByPersonKey(PersonKey pk, FILE *values) ( PersonInfo out; rewind(values) ; fseek(values, pk.offset, SEEK_SET); fread(&out, sizeof(PersonInfo), 1, value); void getPersonsInfo(PersonKey *keys, FILE *values, int size) ( int index; PersonInfo p ; do ( printf("요소의 위치를 ​​입력하세요. 잘못된 인덱스 인쇄를 종료하려면: "); scanf("%d", &index); if (index< 0 || index >= size) ( printf("잘못된 인덱스"); return; ) p = readInfoByPersonKey(keys, value); printInfo(p); ) 동안 (1); ) void main() ( int size; int i; PersonKey *keys = NULL; FILE **files = openFiles("C:/c/keys.bin", "C:/c/values.bin"); if ( files == 0) ( printf("파일 열기 오류"); goto FREE; ) createPersons(files, files); key = readKeys(files, &size);< size; i++) { printKey(keys[i]); } getPersonsInfo(keys, files, size); fclose(files); fclose(files); FREE: free(files); free(keys); _getch(); }

바이너리 파일은 컴퓨터에 있는 모든 파일입니다. 컴퓨터 및 관련 미디어의 모든 정보는 비트(따라서 이름)로 기록됩니다. 그러나 비교를 위해 텍스트 파일은 확장명(가장 간단한 것 - 메모장에서도 가능)에 해당하는 리더에서 읽을 수 있지만 실행 파일은 읽을 수 없습니다. 실제로 txt 파일은 동일한 바이너리 파일이지만 바이너리 파일의 내용을 여는 문제에 대해 이야기할 때 압축된 데이터뿐만 아니라 실행 파일을 의미합니다.

필요할 것이예요

  • - 16진수 편집 프로그램.

지침

  • 내용을 바이너리 형식으로 나타내는 파일 편집기인 Hex Edit 프로그램을 하드 드라이브에 다운로드하세요. 시작 파일을 두 번 클릭하여 프로그램을 엽니다. 이 소프트웨어를 사용하면 바이너리 파일을 실시간으로 읽고, 내용을 변경하고, 항목을 추가하는 등의 작업을 수행할 수 있습니다. 이 환경에서 완벽하게 작업하려면 바이너리 파일의 일반적인 개념에 대해 조금 알아야 합니다.
  • 프로그램 창은 익숙한 메뉴와 버튼이 있는 패널, 편집 중인 파일 본문, 북마크 및 상태 표시줄 등 일반 편집기와 크게 다르지 않습니다. 파일 메뉴를 통해 또는 패널에서 해당 아이콘을 클릭하여 바이너리 파일을 엽니다. 바이너리 파일은 숫자와 문자가 포함된 문자열 형태로 여러분 앞에 나타날 것입니다. 이러한 문자를 텍스트 파일에 인쇄된 데이터와 혼동하지 마십시오. 문자를 삭제하여 편집할 수도 있지만 이 경우 파일의 일부인 데이터가 있는 셀을 삭제하게 됩니다.
  • 파일 내용을 변경합니다. 응용 프로그램은 더 쉬운 검색을 위해 파일의 중요한 부분을 표시할 수 있으며 이진 코드를 그래픽으로 표시하기 위한 유연한 설정도 제공합니다. 파일의 코드를 보려면 컨텐츠 보기를 ASCII+IBM/OEM 모드로 전환하십시오. 파일에 잘못된 줄을 입력하면 올바르게 작동하지 않아 개인용 컴퓨터 운영 체제에 심각한 결과를 초래할 수 있습니다.
  • 변경 사항을 저장합니다. 이러한 유형의 파일 편집에 대한 경험이 없다면 변경 후 파일이 열리지 않고 작동을 거부한다는 사실에 대비하십시오. 제대로 만들기 전에 몇 장의 사본을 엉망으로 만들 가능성이 높습니다. 내용이 변경되지 않도록 원본 파일에 대한 모든 변경 사항을 저장하지 마십시오.
  • 지금까지 고려한 예에서는 정보를 파일로 형식화된 입력/출력하는 방법을 보여주었습니다. 숫자의 형식화된 파일 입/출력은 파일의 크기와 양이 작을 경우나 비프로그래밍 방식으로 파일을 볼 수 있는 기능을 제공해야 하는 경우에만 사용하는 것이 좋습니다. 물론 그렇지 않으면 문자열보다는 컴퓨터의 OP와 같은 방식으로 숫자를 저장하는 바이너리 I/O를 사용하는 것이 훨씬 더 효율적입니다. 정수(int) 또는 실수(float) 값은 메모리에서 4바이트를 차지하고, double 값은 8바이트, char 값은 1바이트를 차지한다는 점을 상기시켜 드리겠습니다. 예를 들어, 텍스트(서식화된) 파일의 숫자 12345는 5바이트를 차지하고 이진 파일에서는 4바이트를 차지합니다.

    바이너리 파일, 즉. 정보가 내부 표현 형식으로 저장된 파일은 소프트웨어에서 나중에 사용할 수 있도록 사용됩니다. 바이너리 파일의 장점은 첫째, 읽고 쓸 때 데이터를 기호 표현 형식에서 내부 표현 형식으로 또는 그 반대로 변환하는 데 시간이 낭비되지 않으며, 둘째 실수의 정밀도가 손실되지 않는다는 것입니다. 형식화된 입력/출력의 경우와 바이너리 입력/출력의 경우 모두 파일의 정보를 "올바르게" 처리하려면 어떤 유형의 데이터가, 어떻게, 어떤 순서로 바이너리에 기록되는지 알아야 합니다. 특히 텍스트 편집기를 사용하여 바이너리 파일을 보는 것은 아무 일도 하지 않기 때문입니다.

    동적 배열의 정수 요소를 이진 파일에 쓰고 이 파일에서 읽는 방법을 보여주는 예를 생각해 보겠습니다.

    #포함하다

    #포함하다

    #포함하다

    네임스페이스 std 사용;

    시합<< "Vvedite kol-vo elementov celochisl. massiva: "; cin >> 엔;

    int *mas = 새로운 int [N];

    for(i=0; 나는

    시합<< " Vvedite " << i << "-i element: "; cin >> 마스[i];

    시합<< "\nIdet zapis dannyh v fail..." << endl;

    ofstream fout("c:\\os\\bin.dat", ios::binary);//만들어진 출구 바이너리 스트림

    if(!fout) (cout<< "\n Oshibka otkrytiya faila!"; getch(); return 1; }

    fout.write(reinterpret_cast (mas), N*sizeof(int));//파일에 배열 쓰기

    fout.close();//스트림 닫기

    시합<< "Dannye uspeshno zapisany!" << endl;

    for(i=0; 나는

    ifstream fin("c:\\os\\bin.dat", ios::binary); //파일을 읽기 위한 스레드 생성

    if(!fin) ( cout<< "\n Oshibka otkrytiya faila!"; getch(); return 1; }

    시합<< "Fail sodergit:" << endl;

    fin.read(reinterpret_cast (mas), N*sizeof(int));//파일에서 배열 읽기

    for(i=0; 나는

    getch(); 0을 반환합니다.

    이 프로그램에서는 write()(ofstream 클래스 메서드) 및 read()(ifstream 클래스 메서드) 함수 사용에 특별한 주의를 기울여야 합니다. 이러한 함수는 데이터를 바이트 단위로 생각하고 데이터 버퍼에서 파일로 특정 수의 바이트를 전송하고 다시 그 반대로 전송하도록 설계되었습니다. 이 함수의 매개변수는 버퍼 주소와 바이트 길이입니다.

    write() 함수는 첫 번째 매개변수에 지정된 바이트 수에서 두 번째 매개변수에 지정된 바이트 수만큼 파일에 쓰도록 설계되었습니다. 구애데이터 버퍼이며 read() 함수는 파일에서 데이터를 읽도록 설계되었습니다. 여기서는 이러한 함수가 char 유형의 데이터 버퍼에서만 작동한다는 점에 유의해야 합니다. 이와 관련하여 이 프로그램에서는 연산자를 사용했습니다. 재해석_캐스트<> 이는 int(mas) 유형의 데이터 버퍼를 char 유형의 버퍼로 변환합니다.

    연산자를 사용하여 캐스팅한다는 점을 기억하는 것이 중요합니다.재해석_캐스트 함수의 첫 번째 매개변수가쓰다() 그리고읽다() 문자 배열이 아닙니다. (결국 문자 유형은1바이트만 사용합니다.)또한 배열이 아닌 개별 변수를 쓰거나 읽어야 하는 경우 참조 메커니즘(데이터 버퍼 주소에 대한 링크)을 사용해야 합니다. 예를 들면 다음과 같습니다.

    ofstream fout(파일 이름, ios::app | ios::binary);

    fout.write(reinterpret_cast (& cb), sizeof(float));

    이제 고려 중인 기능의 두 번째 매개변수에 대해 논의할 필요가 있습니다. 이 프로그램에서는 두 번째 매개변수로 N*sizeof(int) 표현식을 사용하여 바이트 수를 계산했습니다. 예를 들어, 5개의 정수 배열 요소가 있는 경우 바이트 수는 20이 됩니다. sizeof() 함수는 매개변수로 지정된 데이터 유형에 할당된 바이트 수를 반환합니다. 예를 들어, 크기( 정수)는 4를 반환합니다.

    따라서 이 예제에 제공된 프로그램을 사용하면 바이너리 형식의 데이터를 bin.dat 파일에 쓰고 이 바이너리 파일에서 읽을 수 있습니다. 또한 이 데이터를 읽은 후 int 유형으로 변환하고 배열 구조를 획득하며 이를 사용하여 모든 작업을 수행할 수 있습니다.

    이제 bin.dat 파일에서 데이터를 읽을 수 있는 프로그램을 작성해야 하며 이 파일에 이진 형식의 정수 배열 요소가 포함되어 있다는 것만 알고 있다고 가정해 보겠습니다. 작성된 요소 수( N ) 우리는 모른다. 프로그램을 만들 때 상수 배열을 사용할 권한이 없습니다. 프로그램 생성 단계에서 메모리를 할당합니다. 이는 잘못된 결과로 이어질 것입니다. N 값이 너무 작으면 배열의 모든 요소가 계산되지 않고 N 값이 너무 크면 추가 셀이 임의의 값으로 채워지기 때문입니다.

    동적으로 메모리를 할당하여 이진 파일에서 정수 배열의 요소를 읽고 읽은 데이터의 현실성을 증명하고 그 합계를 계산할 수 있는 프로그램의 예를 고려해 보겠습니다.

    #포함하다

    #포함하다

    #포함하다

    네임스페이스 std 사용;

    int N, i, sum=0, dfb; //dfb - 파일 길이(바이트)

    ifstream fin("c:\\os\\bin.dat", iOS::바이너리);

    if(!fin) ( cout<< "Oshibka otkrytiya faila!"; getch(); return 1; }

    fin.seekg(0, ios::end);//읽는 위치를 파일의 끝(끝에서 0바이트)으로 설정합니다.

    dfb = fin.tellg();//파일 위치의 끝 값을 가져옵니다(바이트 단위).

    N=dfb/4;//정수가 4바이트를 차지한다는 것을 알고 숫자의 수를 계산합니다.

    int *arr = 새로운 int [N];//동적 배열 생성

    fin.seekg(0, ios::beg);//데이터를 읽기 전에 현재 위치를 파일의 시작 부분으로 이동합니다.

    fin.read(reinterpret_cast (arr), dfb);

    시합<< "Iz faila schitano " << N << " elementov:" << endl;

    for(i=0; 나는

    for(i=0; 나는

    시합<< "\n Ih summa = " << sum;

    getch(); 0을 반환합니다.

    ifstream 클래스의 메소드인 seekg(),tellg() 함수를 적극적으로 활용한 이 프로그램을 자세히 살펴보자. 여기서 주목해야 할 점은 파일이 열릴 때 모든 파일은 소위 현재 읽기 또는 쓰기 위치와 연결됩니다.. 읽기 위해 파일을 열면 이 기본 위치는 파일의 시작 부분으로 설정됩니다. 그러나 파일의 임의 위치에서 시작하여 읽고 쓰기 위해서는 수동으로 위치를 제어해야 하는 경우가 많습니다. seekg() 및 Tellg() 함수를 사용하면 현재 읽기 포인터를 설정하고 확인할 수 있으며, Seekp() 및 Tellp() 함수는 쓰기 포인터에 대해 동일한 작업을 수행합니다.

    seekg(1_parameter, 2_parameter) 메서드는 2nd_parameter에 지정된 위치를 기준으로 1_parameter에 지정된 바이트 수만큼 파일에서 현재 읽기 위치를 이동합니다. 2_parameter는 다음 세 가지 값 중 하나를 사용할 수 있습니다.

    ios::beg – 파일의 시작 부분부터;

    ios::cur – 현재 위치에서;

    ios::end – 파일의 끝부터.

    여기서 beg, cur 및 end는 ios 클래스에 정의된 상수이며 기호::는 이 클래스에 대한 액세스 작업을 나타냅니다. 예를 들어, 운영자 fin.seekg(-10, ios::end);파일의 현재 읽기 위치를 파일이 끝나기 전 10바이트로 설정할 수 있습니다.

    이제 프로그램 작동에 대한 설명으로 돌아가겠습니다. 파일에 기록된 숫자의 개수를 모른다는 사실을 바탕으로 먼저 숫자의 개수를 알아내야 합니다. 이렇게 하려면 다음을 사용합니다. fin.seekg(0, ios::end);파일의 끝으로 이동하고 Tellg() 함수를 사용하여 파일 길이를 바이트 단위로 dfb 변수에 반환합니다. Tellg() 함수는 현재 포인터 위치를 바이트 단위로 반환합니다. 우리는 하나의 정수 길이를 바이트(4바이트) 단위로 알고 있으므로 파일 길이(바이트)를 알면 파일에 기록된 숫자 수를 쉽게 계산할 수 있습니다( N=dfb/4;). 숫자 수를 알아낸 후 read() 함수를 사용하여 데이터 읽기를 시작하기 위해 동적 배열을 만들고 파일의 시작 부분으로 이동합니다. 지정된 수의 데이터 바이트(dfb)가 데이터 버퍼(arr)로 전송된 후 이러한 방식으로 읽은 데이터는 배열 구조를 획득하고 모든 코드 작업 및 변환에 완전히 적합해집니다.

    파일 설명 및 내부 표현

    파일이 서로 다릅니다. 컴퓨터에 저장된 모든 파일에는 특별한 속성이 있습니다. 한 파일을 다른 파일과 구별할 수 있게 해주는 특별한 설명 방법: 1) 이름; 크기 2; 3) 날짜와 시간 4) 아이콘.

    각 파일에는 이름(파일 이름)이 있습니다. 파일 이름은 해당 내용을 설명하거나 해당 파일의 용도를 제안합니다. 이름은 파일이 생성될 때 파일에 할당됩니다. 이는 모든 파일에 적용됩니다.

    각 파일에는 물리적 크기가 있습니다. 파일은 일부 컴퓨터 메모리와 일부 디스크 공간을 차지합니다.

    파일이 생성되면 운영 체제는 파일에 생성 날짜와 시간을 나타내는 스탬프를 찍습니다. 이를 통해 파일을 날짜와 시간별로 정렬하여 컴퓨터에 정리할 수 있습니다. 파일이 업데이트되거나 수정된 ​​날짜와 시간도 기록됩니다.

    각 파일은 우리가 보는 파일 아이콘과 밀접하게 관련된 특정 유형입니다. 파일 형식은 내용에 따라 다릅니다. 각 프로그램은 생성된 문서에 특정 유형과 해당 아이콘을 할당합니다.

    파일 크기는 메모리 용량과 마찬가지로 바이트 단위로 측정됩니다.

    파일 크기는 0바이트일 수 있습니다. 이는 파일이 존재하지만 아무것도 포함하지 않음을 의미합니다. S 최대 파일 크기는 4GB입니다. 그러나 이렇게 큰 파일은 매우 드뭅니다.

    특히 파일에 생성된 시간과 날짜를 할당하려면 컴퓨터에 내장된 시계가 필요합니다. 이는 이 시계를 올바르게 설정하는 것이 얼마나 중요한지 설명합니다. 시스템 파일, 숨김 파일, 읽기 전용 파일, 아카이브 파일 등과 같이 파일을 설명하는 추가 속성도 있습니다. 운영 체제가 이 문제를 자체적으로 처리합니다.

    각 파일에는 고유한 인덱스가 있습니다. 색인에는 파일에 액세스하는 모든 프로세스에 필요한 정보가 포함되어 있습니다. 잘 정의된 시스템 호출 세트를 사용하고 정규화된 파일 이름 역할을 하는 문자열로 파일을 식별하여 액세스 파일을 처리합니다. 각 복합 이름은 파일을 고유하게 식별하므로 커널이 이름을 파일 인덱스로 변환합니다. 인덱스에는 디스크에 파일 정보가 있는 주소 테이블이 포함됩니다. 디스크의 각 블록은 고유한 번호로 주소가 지정되므로 이 테이블은 디스크 블록 번호 모음을 저장합니다. 유연성을 높이기 위해 커널은 파일에 한 번에 한 블록씩 추가하여 파일 정보가 파일 시스템 전체에 분산되도록 합니다. 그러나 이 레이아웃은 데이터 검색 작업을 복잡하게 만듭니다. 주소 테이블에는 파일에 속한 정보를 포함하는 블록 번호 목록이 포함되어 있지만 간단한 계산을 통해 인덱스에 있는 파일 블록의 선형 목록을 관리하기 어렵다는 것을 알 수 있습니다. 작은 인덱스 구조가 큰 파일 작업을 허용하도록 하기 위해 디스크 블록 주소 테이블이 구조와 일치하게 됩니다.

    텍스트 및 바이너리 파일

    파일을 사용하면 사용자는 키보드를 사용하지 않고도 디스크에서 직접 대량의 데이터를 읽을 수 있습니다. 파일에는 두 가지 주요 유형이 있습니다. 텍스트와 바이너리.

    텍스트임의의 문자로 구성된 파일이 호출됩니다. 각 행은 "로 끝나는 줄로 구성됩니다. 줄 끝". 파일 자체의 끝은 " 기호로 표시됩니다. 파일 끝". 텍스트 편집기를 사용하여 볼 수 있는 텍스트 파일에 정보를 쓸 때 모든 데이터는 문자 유형으로 변환되어 문자 형태로 저장됩니다.

    바이너리로 엑스파일정보는 특정 크기의 블록 형태로 읽고 쓰여지며, 여기에는 모든 유형과 구조의 데이터가 저장될 수 있습니다.

    파일 작업을 위해서는 다음과 같은 특수 데이터 유형이 필요합니다. 스트림. ifstream 스트림은 읽기 모드에서 파일 작업에 사용되고 ifstream은 쓰기 모드에서 사용됩니다. 쓰기 및 읽기 모드에서 파일을 작업하려면 ifstream 스트림이 사용됩니다.

    C++ 프로그램에서 텍스트 파일로 작업할 때 ifstream 및 iostream 라이브러리를 포함해야 합니다.

    텍스트 파일에 데이터를 쓰려면 다음을 수행해야 합니다. 1) 유형 변수 설명 스트림의 열려 있는; 3) 정보를 파일로 출력합니다. 4) 파일을 반드시 닫아주세요.

    텍스트 파일에서 데이터를 읽으려면 다음을 수행해야 합니다.

    1) 유형 변수 설명 ifstream; 2) 함수를 사용하여 파일을 엽니다. 열려 있는; 3) 파일에서 정보를 읽습니다. 각 데이터를 읽을 때 파일 끝에 도달했는지 확인해야 합니다. 4) 파일을 닫습니다.

    위에서 설명한 모든 예에서 "r" 및 "w" 모드의 fopen() 함수는 각각 읽기 및 쓰기를 위해 텍스트 파일을 엽니다. 즉, 캐리지 리턴 '\r'과 같은 일부 텍스트 서식 문자는 별도의 문자로 읽을 수 없지만 파일에는 존재합니다. 이는 파일 텍스트 모드의 기능입니다. 파일 내용에 대한 보다 "세밀한" 작업을 위해 파일 내용을 일련의 바이트로 나타내는 바이너리 모드가 있습니다. 여기서 가능한 모든 제어 코드는 단지 숫자입니다. 이 모드에서는 텍스트 모드에서 사용할 수 없는 제어 문자를 제거하거나 추가할 수 있습니다. 바이너리 모드에서 파일을 열기 위해 fopen() 함수는 읽기 및 쓰기를 위해 각각 "rb" 및 "wb"와 동일한 마지막 매개변수와 함께 사용됩니다.



    질문이 있으신가요?

    오타 신고

    편집자에게 전송될 텍스트: