C语言文本流和二进制流

Python015

C语言文本流和二进制流,第1张

一、文本流和二进制流

在C中引入了流(stream)的概念。它将数据的输入输出看作是数据的流入和流出,这样不管是磁盘文件或者是物理设备(打印机、显示器、键盘等),都可看作一种流的源和目的,视他们为同一种东西,而不管其具体的物理结构,即对他们的操作,就是数据的流入和流出。这种把数据的输入输出操作对象,抽象化为一种流,而不管它的具体结构的方法很有利于编程,而涉及流的输出操作函数可用于各种对象,与其具体的实体无关,即具有通用性。

在C中流可分为两大类,即文本流(text stream)和二进制流(binary stream)。所谓文本流是指在流中流动的数据是以字符形式出现。在文本流中,'\n'被换成回车CR和换行LF的代码0DH和0AH。而当输出时,则0DH和0AH本换成'\n'。

二进制流是指流动的是二进制数字序列,若流中有字符,则用一个字节的二进制ASCII码表示,若是数字,则用一个字节的二进制数表示。在流入流出时,对\n符号不进行变换。例如2001这个数,在文本流中用其ASCII码表示为:

'2' '0' '0' '1'

| | | |

50 48 48 49

共占4字节。而在二进制流中则表示为:00000111 11010001 用十六进制就是07D1。只占两字节。

由此看出,二进制流比文本流节省空间,且不用进行对\n的转换,这样可以大大加快流的速度,提高效率。因而,对于含有大量数字信息的数字流,可以采用二进制流的方式;对于含有大量字符信息的流,则采用文本流的方式。

二、流和文件

在C语言中流就是一种文件形式,它实际上就表示一个文件或设备(从广义上讲,设备也是一种文件)。把流当作文件总觉得不习惯,因而有人称这种和流等同的文件为流式文件,流的输入输出也称为文件的输入输出操作。当流到磁盘而成为文件时,意味着要启动磁盘写入操作,这样流入一个字符(文本流)或流入一个字节(二进制流)均要启动磁盘操作,将大大降低传输效率(磁盘是慢速设备),且降低磁盘的使用寿命。为此,C语言在输入输出的使用使用了缓冲技术,即在内存为输入的磁盘文件开辟了一个缓冲区(缺省为512字节),当流到该缓冲区装满后,再启动磁盘一次,将缓冲区内容装到磁盘文件中去。读取文件也是类似。

在C语言中将此种文件输入输出操作称为标准输入输出,或称流式输入输出(因这种输入输出操作是ANSI C推荐的标准)。还有一种是不带缓冲文件输入输出,称为非标准文件输入输出或低级输入输出,它将由DOS直接管理。关于这两种输入输出文件系统下节将会介绍。

三、文件FILE的数据结构

typedef struct

{

short level

unsigned flags

char fd

unsigned char hold

short bsize

unsigned char *buffer

unsigned char *curp

unsigned istemp

short token

}FILE

这是Turbo C中使用的定义(在stdio.h文件中),不同的C编译器,可能使用不同的定义,但基本含义变化不会太大。

flags: 是一个10位的标志字,其具体含义如下:

位 代表符号 含义

0 _F_READ 读

1 _F_WRIT 写

2 _F_BUF 由fclose释放缓冲区

3 _F_LBUF 行缓冲

4 _F_ERR 出错标志

5 _F_EOF EOF文件尾标志

6 _F_BIN 二进制方式

7 _F_IN 在进行输入

8 _F_OUT 在进行输出

9 _F_TERM 文件是一个终端

其他各字段内容以及flags字段内各位所的功能,请参照其他一些资料。这里不多说了,它目前并不是我们要求的内容。

应该注意,不要把文件指针和FILE结构指针混为一谈,它们代表两个不同的地址。文件指针指出了对文件当前读写的数据位置,而FILE结构指针是指出了打开文件所对应的FILE结构在内存中的地址,这个指针它实际本身也包含了文件指针的信息。流指针中的各字段是供C语言内部使用的,用户不应该存取它的任何字段。用fwrite写的文件为二进制流,所以当用文本打开文件时数字变成了乱码.解决办法是:1.改用fprintf 或者用itoa等格式化数字为字符串,然后用fwrite#include "stdio.h"

#include "iostream.h"

struct student

{

int no

char name[10]

}a[4]

void save()

{ int i

FILE *p

if ((p=fopen("1.txt","w"))==NULL)

{cout<<"\n"<<"can not open file"<br>}

for(i=1i<4i++)

{

fprintf(p, "%d%10s\t", a[i].no, a[i].name)

}

fclose(p)

}

void main()

{

printf("please input no name")

int i

for (i=1i<4i++)

{scanf("%d%s",&a[i].no,a[i].name)<br>}

for (i=1i<4i++)

{cout<<a[i].no<<a[i].name<br>}

save()

}函数说明:fprintf int fprintf(FILE *fp, char *format,…)

stdio.h

将格式化数据写入流式文件中

fp 文件指针

format 格式化字符,同函数printf()中的格式控制字符 … 要显示的数据项列表2.用另一种方法进行文件输入编码可消除这些问题。

主要是用C++,添加头文件# include "fstream.h"。参考代码如下:

#include "stdio.h"

#include "iostream.h"

# include "fstream.h"

struct student

{int no<br>char name[10]<br>}a[4]

void main()

{

ofstream ofile ( "file.txt", ios::out )

printf("please input 4 students' names\n")

int i

for (i=0i<4i++)

{scanf("%s",a[i].name)<br>a[i].no=i+1<br>ofile <<a[i].no<<" "<<a[i].name<<endl<br>}

}

我提个思路。字节是计算机操作的实际最基本单位,任何地址只能精确到字节而无法精确到位的。

所以LZ一定要将位整合到以字节为单位才能进行其他操作。可以这么做,将二进制流按照每8位一个单位装成一个字节,并明确好字节之间的顺序,字节内高低位的顺序,最后末尾不足的要塞一些没意义的但LZ要知道的位凑足成字节。这里面当然必须反复用到位运算。

通常的做法是字节之间的顺序按照自然字节地址由小到大顺序,字节内低位在前。比如对于已经整合成字节的字节流a[N],查看第 i 个位(从0起)的算法是 a[i/8] &(1<<(i%8))。

不同的位序约定会稍有不同位操作

二进制打开文件fopen(filename,"rd")

二进制读可以用 fscanf()

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main(void)

{

FILE * fp

char * filename = "test.dat"

int i

char str[128]

if( (fp=fopen(filename,"rb"))== NULL )

{