读写二进制文件(转载:来自http://www.tzwhx.com)

读写二进制文件通常要使用FileStream类。

1. FileStream

FileStream实例用于读写文件中的数据。要构造FileStream实例,需要以下4条信息:

要访问的文件。

表示如何打开文件的模式。例如,创建一个新文件或打开一个现有的文件。如果打开一个现有的文件,写入操作是应改写文件原来的内容,还是添加到文件的末尾?

表示如何访问文件的访问方式—— 是只读、只写,还是读写?

共享访问—— 换言之,是独占访问文件,还是允许其他流同时访问文件?如果允许其他流同时访问文件,则这些流是只读、只写,还是读写文件?

第一条信息通常用一个包含文件完整路径名的字符串来表示,本章只考虑需要该字符串的构造函数。除了这些构造函数外,其他的构造函数用老式的Windows-API文件句柄来处理文件。其余3条信息分别由3.NET枚举FileModeFileAccess FileShare来表示,这些枚举的值应是自我解释性的,如表30-3所示。

  30-3

 

FileMode

AppendCreateCreateNewOpenOpenOrCreateTruncate

FileAccess

ReadReadWriteWrite

FileShare

InheritableNoneReadReadWriteWrite

注意,对于FileMode,如果要求的模式与文件的现有状态不一致,就会抛出一个异常。如果文件已经不存在,AppendOpenTruncate会抛出一个异常,如果文件存在,CreateNew会抛出一个异常。CreateOpenOrCreate可以处理这两种情况,但Create会删除现有的文件,创建一个新的空文件。FileAccess FileShare枚举是按位标志,所以这些值可以与C#的按位OR运算符|合并使用。

FileStream有许多构造函数,其中3个最简单的构造函数如下所示。

// creates file with read-write access and allows other streams read access

FileStream fs = new FileStream(@"C:"C# Projects"Project.doc",

FileMode.Create);

 

// as above, but we only get write access to the file 

FileStream fs2 = new FileStream(@"C:"C# Projects"Project2.doc",

  FileMode.Create, FileAccess.Write);

 

// as above but other streams don~t get any access to the file while

// fs3 is open

FileStream fs3 = new FileStream(@"C:"C# Projects"Project3.doc",

  FileMode.Create, FileAccess.Write, FileShare.None);

从这段代码中可以看出,构造函数的这些重载方法会把FileAccess.ReadWrite FileShare.Read的默认值作为第34个参数,也可以以多种方式从FileInfo实例中创建一个文件流:

FileInfo myFile4 = new FileInfo(@"C:"C# Projects"Project4.doc");

FileStream fs4 = myFile4.OpenRead();

 

FileInfo myFile5= new FileInfo(@"C:"C# Projects"Project5doc");

FileStream fs5 = myFile5.OpenWrite();

 

FileInfo myFile6= new FileInfo(@"C:"C# Projects"Project6doc");

FileStream fs6 = myFile6.Open(FileMode.Append, FileAccess.Write,

  FileShare.None);

 

FileInfo myFile7 = new FileInfo(@"C:"C# Projects"Project7.doc");

FileStream fs7 = myFile7.Create();

FileInfo.OpenRead()提供的流只能读取现有的文件,而FileInfo ().OpenWrite()可以进行读写访问。FileInfo.Open()允许显式指定模式、访问方式和文件共享参数。

使用完一个流后,就应关闭它:

fs.Close();

关闭流会释放与它相关的资源,允许其他应用程序为同一个文件设置流。在打开和关闭流之间,可以读写其中的数据。FileStream有许多方法可以进行这样的读写。

ReadByte()是读取数据的最简单的方式,它从流中读取一个字节,把结果转换为一个0~255之间的一个整数。如果到达该流的末尾,就返回–1

int NextByte = fs.ReadByte();

如果喜欢一次读取多个字节,可以调用Read()方法,它可以把特定数量的字节读入到一个数组中。Read()方法返回实际读取的字节数—— 如果这个值是0,就表示到达了流的尾端。下面是读入Byte数组ByteArray的一个示例:

int nBytesRead = fs.Read(ByteArray, 0, nBytes);

Read()的第二个参数是一个偏移值,使用它可以要求Read读取的数据从数组的某个元素开始填充,而不是从第一个元素开始。第三个参数是要读入数字的字节数。

如果要给文件写入数据,可以使用两个并行方法WriteByte() Write()WriteByte()方法把一个字节写入流:

byte NextByte = 100;

fs.WriteByte(NextByte);

另外,Write()则写入一个字节数组。例如,如果用一些值初始化前面的ByteArray,就可以使用下面的代码写入数组的前nBytes 个字节:

fs.Write(ByteArray, 0, nBytes);

Read()一样,第二个参数可以从数组的某个元素开始写入,而不是从第一个元素开始。WriteByte() Write()都没有返回值。

除了这些方法以外,FileStream还有其他方法和属性可以处理簿记任务,例如确定流中有多少字节,锁定流或刷新缓冲区等。其他方法通常不是基本读写数据所必须的,如果您需要使用它们,可以参阅SDK文档说明书。

2. BinaryFileReader示例

下面编写一个示例BinaryFileReader说明FileStream类的用法。这个示例可以读取和显示任何文件。在Visual Studio .NET中创建一个Windows应用程序,添加一个菜单项,它可以打开一个标准的OpenFileDialog,要求用户指定要读取的文件,然后把该文件显示为二进制码。在读取二进制文件时,需要显示非打印(non-printable)字符。此时可以在多行文本框中逐个显示文件中的每个字节,每行显示16个字节。如果字节表示一个可显示的ASCII字符,就显示该字符,否则就以16进制的格式显示该字节的值。在这两种情况下,显示的文本之间都用空格隔开,这样每个显示的字节都占用4列,显得它们的排列非常整齐。

30-8BinaryFileReader应用程序查看文本文件时所显示的情况(因为BinaryFileReader可以查看任何文件,所以可以在文本文件和二进制文件中使用它)。本示例读取一个基本的ASP.NET页面(.aspx)

  30-8

显然,这个格式更适合于查看单个字节的值,而不是显示文本。本章的后面会开发一个专门用于读取文本文件的示例—— 然后就可以查看文件中的内容了。另一方面这个示例的优点是可以查看任何文件的内容。

这个示例没有说明如何写入文件。这是因为我们不希望把文本框中的内容转换为二进制流,增加程序的复杂性。在后面开发可读写文本文件的示例中,将介绍如何写入文件。

下面看看获得这些结果的代码。首先,需要一个额外的using语句,因为除了System.IO之外,这个示例还要使用System.Text 命名空间中的StringBuilder类,以构造文本框中的字符串:

using System.IO;

using System.Text;

接着,给主窗体类添加两个字段,一个字段表示文件对话框,另一个是表示当前文件的路径的字符串:

public class Form1 : System.Windows.Forms.Form

{

  private OpenFileDialog chooseOpenFileDialog = new OpenFileDialog();

  private string chosenFile;

还需要添加一些标准的Windows窗体代码,来处理菜单和文件对话框的处理程序:

  public Form1()

  {

InitializeComponent();

menuFileOpen.Click += new EventHandler(OnFileOpen);

chooseOpenFileDialog.FileOk += new

  CancelEventHandler(OnOpenFileDialogOK);

  }

  void OnFileOpen(object Sender, EventArgs e)

  {

chooseOpenFileDialog.ShowDialog();

  }

  void OnOpenFileDialogOK(object Sender, CancelEventArgs e)

  {

   chosenFile = chooseOpenFileDialog.FileName;

this.Text = Path.GetFileName(chosenFile);

DisplayFile();

  }

可以看出,用户单击OK,在文件对话框中选择一个文件后,就会调用方法DisplayFile(),该方法读取所选中的文件:

  void DisplayFile()

  {

int nCols = 16;

FileStream inStream = new FileStream(chosenFile, FileMode.Open,

FileAccess.Read);

long nBytesToRead = inStream.Length;

if (nBytesToRead > 65536/4)

  nBytesToRead = 65536/4;

 

int nLines = (int)(nBytesToRead/nCols) + 1;

string [] lines = new string[nLines];

int nBytesRead = 0;

 

for (int i=0 ; i

{

  StringBuilder nextLine = new StringBuilder();

  nextLine.Capacity = 4*nCols;

 

  for (int j = 0 ; j

  {

int nextByte = inStream.ReadByte();

nBytesRead++;

if (nextByte < 0 || nBytesRead > 65536)

  break;

char nextChar = (char)nextByte;

if (nextChar < 16)

  nextLine.Append(" x0" + string.Format("{0,1:X}",

(int)nextChar));

else if

  (char.IsLetterOrDigit(nextChar) ||

  char.IsPunctuation(nextChar))

  nextLine.Append("  " + nextChar + " ");

else

  nextLine.Append(" x" + string.Format("{0,2:X}",

(int)nextChar));

  }

  lines[i] = nextLine.ToString();

}

inStream.Close();

this.textBoxContents.Lines = lines;

  }

在这个方法中进行了许多工作,首先为所选文件实例化一个FileStream,指定要打开一个现有文件进行读取。然后确定要读取多少个字节,和显示多少行。这个字节数一般是文件中的字节数。文本框最多只能显示65 536个字符,但以我们选择的格式,文件中的每个字节可以显示4个字符,因此如果文件的字节数大于65 536/4 = 16 384,就需要覆盖一些已显示的字节。

注意:

如果要在这种环境下显示较长的文件,就需要使用System.Windows.Forms命名空间中的 RichTextBox类。RichTextBox类似于文本框,但它有更高级的格式化功能,在显示的文本大小方面没有限制,此处使用TextBox,是为了让示例比较简单,用户可以只考虑读取文件的过程。

在该方法中,有相当多的部分是两个嵌套的循环,它们构造出每个要显示的文本行。我们使用StringBuilder类来构造每一文本行,其性能方面的原因是:可以把每个字节的合适文本追加到表示每行文本的字符串上。如果一行上有一个新字符串,并复制构造该行时的一半字符,则不仅要花很多时间分配字符串,还会浪费堆上的许多内存。注意,可显示的字符是指字母、字或标点符号,这与相关的静态方法System.Char指定的一样。值小于16的字符都不是可显示的字符,因此回车符(13)和换行符(10)都是二进制字符(如果这些字符单独占一行,多行文本框不能正确显示它们)

另外要注意的两点是:使用属性窗口,把文本框的字体改为固定宽度的字体——我们选择Courier New 9pt常规字体,并把文本框设置为有水平和垂直滚动条。

最后,选择流,把文本框的内容设置为已建立的字符串数组。

posted on 2009-01-08 11:32  泡泡雪碧  阅读(2051)  评论(0编辑  收藏  举报

导航