⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 23.txt

📁 一個計算機系教授的上課講義 主要是教導學生使用C語言編寫程序
💻 TXT
字号:
CS 1355
Introduction to Programming in C
Monday 2006.12.4
Lecture notes (at http://r638-2.cs.nthu.edu.tw/C/notes/23.txt)

Today: Chapter 11 File I/O 

What is a file?
- an array of bytes
  stored on disk, has a name
How to read/write a file?
- through "file handle"
  - open a file for reading or writing
  - use read/write (could be printf/scanf) functions with the handle
  - either sequentially or by random access
  - close the file
The C language does not know anything about files!
- all done in the library (e.g., stdio.h)
- actually implemented by the operating system
Functions include
	printf, getchar, scanf, putchar => these assume stdin, stdout
More general versions are
	fprintf, fgetc, fscanf, fputc => work with stdin, stdout too

In general, a file has to be opened using
	FILE *fh;
	fh = fopen("fileName", "r") /* "r" for reading */
				/* or "w" for writing */
fh is called the "file handle"
then, pass fh to different function calls
- stdin and stdout are also of type FILE*
- stdin and stdout are automatically opened!
- stdin and stdout are implicitly passed to the non-f versions

Example:
	printf("hello world");
is really the same as
	fprintf(stdout, "hello world");
Similarly,
	scanf("%d", &i);
is really the same as
	fscanf(stdin, "%d", &i);
The file handle is usually passed as the first parameter to these
file functions. But some pass as second parameter
	fputc('a', stdout);  /* like putchar('a') */


First step: open a file
	fh = fopen("myFile.txt", "r"); /* opens for reading */
   or,  fh = fopen("myFile.txt", "w"); /* opens for writing */
Result:
- successful, then fh is a valid file handle to the file
- if fail, fh is == NULL!
  - file might not exist (for reading) (for writing it's ok)
  - file might exist but you have no permission
    (e.g., trying to write to a read-only file)
The "r", "w" is called the "file open mode"
- "r" reading
- "w" writing (create if necessary; or delete all content and write)
- "a"   append (write to the end; don't delete existing content)
- "r+"  reading and writing (don't delete old content first)
- "w+"  reading and writing but delete old content first
- "a+"  append but allow reading also
- "rb"  read as binary file
- "wb"  write as binary file
- "ab"  append as binary file
etc

After your program finishes accessing a file, 
be sure to close it using fclose(fh)!!
why? because files are "buffered"
- it is slow to access a disk
- So, there is a buffer for files
  - read in multiple "blocks" of data at a time
  - write to buffer first, then "flush" the data to disk 
    one block at a time.
- This means after you write to a file,
  it might not actually write to disk immediately!

To make sure it writes to disk, call either
- fflush(fh) /* allows you to continue accessing the file */
- fclose(fh) /* close the file, so fh is no longer valid! */

Example: grade book
#include <stdio.h>
#define GRADEFILE "grade.txt"
int main() {
	FILE *fh = fopen(GRADEFILE, "r");
	int grade, total = 0, count = 0;
	if (fh == NULL) {
		fprintf(stderr, "Cannot open file %s\n", GRADEFILE);
		return 1; /* an error code */
	}
	/* file opened successfully */
	while (fscanf(fh, "%d", &grade) != EOF) {
		total += grade;
		count++;
	}
	fclose(fh);
	if (count > 0) {
		printf("Average is %f\n", (float) total / count);
	}
}
---
This uses fopen(), fprintf(), fscanf(), fclose() 

One thing you can do with real files (instead of keyboard)
is you can rewind a file!

before you close a file, or even after you reach EOF, you can say
	rewind(fh);
this lets you read the file from the beginning again!


Concepts:
- text vs binary data representation
- sequential vs. random access

You can store numbers different ways
- numerals encoded as ASCII character strings 
- (binary) data as stored in the variables

Difference:
- text string is 
  - variable length (e.g., 
    "0" is one byte plus delimiter, which may be a blank or a 
        punctuation mark,
    "100" is 3 byte + delimiter,
    "10000" is 5 bytes + delimiter, etc)
  - use text-oriented I/O calls
    printf() to print number (int, etc) as text string
    scanf() to scan from text string into variables (int, etc)
  - advantage:
    - human readable, typable
    - handles arbitrarily large numbers
    - "portable" (works on different machines, no problem with endian)
  - disadvantages
    - sequential access only,
    - difficult to do random access with variable length data
      => unless perfectly aligned using space/tab, so the position is
         perfectly predictable
    - requires parsing, could be slower
    - might have "syntax error" (e.g., what if user types "hello world"
      when you expect a decimal numeral?)
- binary file 
  - can be fixed length
    e.g., if an int is 4 bytes,
    then you can just write 4 bytes of data that represent that int
  - advantages:
    - predictable, can access like array, just need to know offset
    - just read the raw data to fill the array, 
      or just write the raw data to disk, no other processing
    - potentially much faster, no "syntax error",
      (unless file size is not multiple of 4 in this case?)
  - disadvantages:
    - not human readable or typable, unless translated by some program
    - endian problem!
      x86 (Intel, AMD) is little endian,
      IBM, Motorola, etc are big endian
      Data not exchangeable 
      (e.g., 0x12345678 gets laid out as 78 56 34 12 on x86,
        as 12 34 56 78 on all others
    - limited precision: once decided on 4 bytes,
      always must use 4 bytes, then maybe convert to 2 bytes or 8 bytes
      depending on the processor
  - Use  fread() and fwrite() routines
    instead of fscanf(), fprintf()
  - use feof() to test end-of-file (both text and binary can use it)

Example: convert from a text file of int to a binary file of int
Use fread() and fwrite():
size_t fread(void *ptr, size_t size, size_t nMembers, FILE* fh);
=> return value is the number of members (== #bytes / size) actually read,
=> *ptr is pointer to any data type (usually an array),
=> size is usually sizeof() element of an array
=> nMembers is usually the number of elements in an array
=> fh is the file handle for a file already opened for reading

size_t fwrite(void *ptr, size_t size, size_t nMembers, FILE* fh);
=> return value and parameters have similar meaning to fread(),
   except for writing instead of reading.

/* Program: tobin.c
 * converts from text numerals to binary int file
 */
#include <stdio.h>
int main(int argc, char **argv) {
	FILE *inFile, *outFile;
	int number;
	int errCode = 0;
	/* usage is programName inFile outFile */
	if (argc != 3) {
		fprintf(stderr, "Usage: %s inFile outFile\n", argv[0]);
		return 1;
	}
	/* try opening inFile for reading, outFile for writing */
	if ((inFile = fopen(argv[1], "r")) == NULL) {
		fprintf(stderr, "Cannot read inFile '%s'\n", argv[1]);
		return 2;
	}
	if ((outFile = fopen(argv[2], "w")) == NULL) {
		fprintf(stderr, "Cannot write outFile '%s'\n", argv[2]);
		return 3;
	}
	/* now we can read and write both files, read one int string,
	   write one int binary */
	while (fscanf(inFile, "%i", &number) == 1) { /* found next number */
		if (fwrite(&number, sizeof(number), 1, outFile) != 1) {
			/* something wrong */
			fprintf(stderr, "failed writing to file\n");
			errCode = 4;
			break;
		}
	}
	if (!errCode && !feof(inFile)) {
		fprintf(stderr, "syntax error in input file\n");
		errCode = 5;
	}
	/* close the files */
	fclose(inFile);
	fclose(outFile);
	return errCode;
}
----
Run it with input file whose content is
0x12
034
1523
17
-1
64

If you have hexdump, you can show the content in hex.

If you run vim on the output file, you will probably get
^@^@^@^R^@^@^@^\^@^@^E?^@^@^@^Q????^@^@^@@
(but it might be different)
^@ is the null byte
^R is 18 (because ^A is 1, ^B is 2, etc)

To recover the human readable version, can do

/* Program: totext.c
 * it does the reverse of tobin.c
 */
#include <stdio.h>
int main(int argc, char **argv) {
        FILE *inFile, *outFile;
        int number;
        if (argc != 3) {
                fprintf(stderr, "Usage: %s inFile outFile\n", argv[0]);
                return 1;
        }
        if ((inFile = fopen(argv[1], "r")) == NULL) {
                fprintf(stderr, "Cannot read inFile '%s'\n", argv[1]);
                return 2;
        }
        if ((outFile = fopen(argv[2], "w")) == NULL) {
                fprintf(stderr, "Cannot write outFile '%s'\n", argv[2]);
                return 3;
        }
        while (fread(&number, sizeof(number), 1, inFile) == 1) {
                fprintf(outFile, "%d\n", number); /* print all as decimal */
        }
        /* close the files */
        fclose(inFile);
        fclose(outFile);
        return 0;
}
===
After you run it on the binary file, the result is an ASCII one:
18
28
1523
17
-1
64
=====

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -