博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
FileStream随机文件访问(访问文件中间某点的数据)
阅读量:4323 次
发布时间:2019-06-06

本文共 5964 字,大约阅读时间需要 19 分钟。

     FileStream对象表示在磁盘或网络路径上指向文件的流。这个类提供了在文件中读写字节的方法,但经常使用StreamReader或 StreamWriter执行这些功能。这是因为FileStream类操作的是字节和字节数组,而Stream类操作的是字符数据。字符数据易于使用, 但是有些操作,比如随机文件访问(访问文件中间某点的数据),就必须由FileStream对象执行

 

 文件位置

     FileStream类维护内部文件指针,该指针指向文件中进行下一次读写操作的位置。在大多数情况下,当打开文件时,它就指向文件的开始位置,但 是此指针可以修改。这允许应用程序在文件的任何位置读写,随机访问文件,或直接跳到文件的特定位置上。当处理大型文件时,这非常省时,因为马上可以定位到 正确的位置。

实现此功能的方法是Seek()方法,它有两个参数:第一个参数规定文件指针以字节为单位的移动距离。第二个参数规定开始计算的起始位置,用SeekOrigin枚举的一个值表示。Seek Origin枚举包含3个值:Begin、Current和End。

例如,下面的代码行将文件指针移动到文件的第8个字节,其起始位置就是文件的第1个字节:

 

aFile.Seek(
8,SeekOrigin.Begin);

 

下面的代码行将指针从当前位置开始向前移动2个字节。如果在上面的代码行之后执行下面的代码,文件指针就指向文件的第10个字节:

 

aFile.Seek(
2,SeekOrigin.Current);

 

注意读写文件时,文件指针也会改变。在读取了10个字节之后,文件指针就指向被读取的第10个字节之后的字节。

也可以规定负查找位置,这可以与SeekOrigin.End枚举值一起使用,查找靠近文件末端的位置。下面的代码会查找文件中倒数第5个字节:

 

aFile.Seek(–
5, SeekOrigin.End);

 

以这种方式访问的文件有时称为随机访问文件,因为应用程序可以访问文件中的任何位置。稍后介绍的Stream类可以连续地访问文件,不允许以这种方式操作文件指针。

 

读取数据

使用FileStream类读取数据不像使用本章后面介绍的StreamReader类读取数据那样容易。这是因为FileStream类只能处理 原始字节(raw byte)。处理原始字节的功能使FileStream类可以用于任何数据文件,而不仅仅是文本文件。通过读取字节数据,FileStream对象可以用 于读取图像和声音的文件。这种灵活性的代价是,不能使用FileStream类将数据直接读入字符串,而使用StreamReader类却可以这样处理。 但是有几种转换类可以很容易地将字节数组转换为字符数组,或者进行相反的操作。

FileStream.Read()方法是从FileStream对象所指向的文件中访问数据的主要手段。这个方法从文件中读取数据,再把数据写入 一个字节数组。它有三个参数:第一个参数是传输进来的字节数组,用以接受FileStream对象中的数据。第二个参数是字节数组中开始写入数据的位置。 它通常是0,表示从数组开端向文件中写入数据。最后一个参数指定从文件中读出多少字节。

下面的示例演示了从随机访问文件中读取数据。要读取的文件实际是为此示例创建的类文件。

试试看:从随机访问文件中读取数据

(1) 在目录C:/BegVCSharp/Chapter22下创建一个新的控制台应用程序ReadFile。

(2) 在Program.cs文件的顶部添加下面的using指令:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

(3) 在Main()方法中添加下面的代码:

           
byte[] byData = 
new 
byte[
200];
            
char[] charData = 
new Char[
200];
            
try
            {
                FileStream aFile = 
new FileStream(
"
C:\\Windows\\ODBC.INI
", FileMode.Open);
                aFile.Seek(
135, SeekOrigin.Begin);
                aFile.Read(byData, 
0
200);
            }
            
catch (IOException e)
            {
                Console.WriteLine(
"
An IO exception has been thrown!
");
                Console.WriteLine(e.ToString());
                Console.ReadKey();
                
return;
            }
            Decoder d = Encoding.UTF8.GetDecoder();
            d.GetChars(byData, 
0, byData.Length, charData, 
0);
            Console.WriteLine(charData);
            Console.ReadKey();

 

示例的说明

此应用程序打开自己的ODBC.INI文件,用于读取。

 FileStream aFile = 
new FileStream(
"
C:\\Windows\\ODBC.INI
", FileMode.Open);

 

下面两行代码实现查找工作,并从文件的具体位置读取字节:

   aFile.Seek(
135,SeekOrigin.Begin);
   aFile.Read(byData,
0,
200);

第一行代码将文件指针移动到文件的第135个字节。第二行将接下来的200个字节读入到byData字节数组中。

 

注意这两行代码封装在try…catch块中,以处理可能抛出的异常。

 

 
try
   {
      aFile.Seek(
135,SeekOrigin.Begin);
      aFile.Read(byData,
0,
100);
   }
   
catch(IOException e)
   {
      Console.WriteLine(
"
An IO exception has been thrown!
");
      Console.WriteLine(e.ToString());
      Console.ReadKey();
      
return;
  }

 

文件IO涉及到的所有操作都可以抛出类型为IOException的异常。所有代码都必须包含异常处理,尤其是操作文件系统时更是如此。

从文件中获取了字节数组后,就需要将其转换为字符数组,以便在控制台显示它。为此,使用System.Text命名空间的Decoder类。此类用于将原始字节转换为更有用的项,比如字符:

Decoder d = Encoding.UTF8.GetDecoder();
d.GetChars(byData, 
0, byData.Length, charData, 
0);

这些代码基于UTF8编码模式创建了Decoder对象。这就是Unicode编码模式。然后调用GetChars()方法,此方法提取字节数组,将它转换为字符数组。完成之后,就可以将字符数组输出到控制台。

 

写入数据

 

向随机访问文件中写入数据的过程与从中读取数据非常类似。首先需要创建一个字节数组;最简单的办法是首先构建要写入文件的字符数组。然后使用Encoder对象将其转换为字节数组,其用法非常类似于Decoder。最后调用Write()方法,将字节数组传送到文件中。

下面构建一个简单的示例演示其过程。

试试看:将数据写入随机访问文件

(1) 在C:/BegVCSharp/Chapter22目录下创建一个新的控制台应用程序WriteFile。

(2) 如上所示,在Program.cs文件顶部添加下面的using指令:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

 

在Main()方法中添加下面的代码:

static 
void Main(
string[] args)
{
   
byte[] byData;
   
char[] charData;
   
try
   {
      FileStream aFile = 
new FileStream(
"
Temp.txt
", FileMode.Create);
      charData = 
"
My pink half of the drainpipe.
".ToCharArray();
      byData = 
new 
byte[charData.Length];
      Encoder e = Encoding.UTF8.GetEncoder();
      e.GetBytes(charData, 
0, charData.Length, byData, 
0
true);
      
//
 Move file pointer to beginning of file.
      aFile.Seek(
0, SeekOrigin.Begin);
      aFile.Write(byData, 
0, byData.Length);
   }
   
catch (IOException ex)
   {
      Console.WriteLine(
"
An IO exception has been thrown!
");
      Console.WriteLine(ex.ToString());
      Console.ReadKey();
      
return;
   }
}

 

(4) 运行该应用程序。稍后就将其关闭。

(5) 导航到应用程序目录 —— 在目录中已经保存了文件,因为我们使用了相对路径。目录位于WriteFile/bin/Debug文件夹。打开Temp.txt文件。可以在文件中看到如图22-3所示的文本。

 

示例的说明

此应用程序在自己的目录中打开文件,并在文件中写入了一个简单的字符串。在结构上这个示例非常类似于前面的示例,只是用Write()代替了Read(),用Encoder代替了Decoder。

下面的代码行使用String类的ToCharArray()静态方法,创建了字符数组。因为C#中的所有事物都是对象,文本“My pink half of the drainpipe.”实际上是一个String对象,所以甚至可以在字符串上调用这些静态方法。

CharData = 
"
 My pink half of the drainpipe. 
".ToCharArray();

下面的代码行显示了如何将字符数组转换为FileStream对象需要的正确字节数组。

Encoder e = Endoding.UTF8.GetEncoder();
e.GetBytes(charData,
0,charData.Length, byData,
0,
true);

 

这次,要基于UTF8编码方法来创建Encoder对象。也可以将Unicode用于解码。这里在写入流之前,需要将字符数据编码为正确的字节格式。在GetBytes()方法中可以完成这些工作,它可以将字符数组转换为字节数组,并将字符数组作为第一个参数(本例中的charData),将该数组中起始位置的下标作为第二个参数(0表示数组的开头)。第三个参数是要转换的字符数量(charData.Length,charData数组中的元素个数)。第四个参数是在其中置入数据的字节数组(byData),第五个参数是在字节数组中开始写入位置的下标(0表示byData数组的开头)。

最后一个参数决定在结束后Encoder对象是否应该更新其状态,即Encoder对象是否仍然保留它原来在字节数组中的内存位置。这有助于以后调用Encoder对象,但是当只进行单一调用时,这就没有什么意义。最后对Encoder的调用必须将此参数设置为true,以清空其内存,释放对象,用于垃圾回收。

之后,使用Write()方法向FileStream写入字节数组就非常简单:

 

aFile.Seek(
0,SeekOrigin.Begin);
aFile.Write(byData,
0,byData.Length);

与Read()方法一样,Write()方法也有三个参数:要写入的数组,开始写入的数组下标和要写入的字节数。

 

以上写数据写的是英文数据,我们知道中文占两个字节,那么如果写入的是中文我们需要计算字节数.我们可以使用以下方式获取一个字符串的字节数:

int   n   =   System.Text.Encoding.GetEncoding( 
"
GB2312 
").GetByteCount( 
"
YourString 
"); 

 还可以用:

string s = 
"
中ABc国
";
int myLength = (System.Text.Encoding.Default.GetBytes(s)).Length;

 

那么如果处理的是中文我们就应该这样写:

 

           
byte[] byData;
            
char[] charData;
            
try
            {
                FileStream aFile = 
new FileStream(
"
Temp.txt
", FileMode.Create);
                charData = 
"
我是禽兽,你是禽兽不如...
".ToCharArray();
                
//
获取字符串字节的长度(注:非字符长度)
                
int length = Encoding.UTF8.GetByteCount(charData);
                byData = 
new 
byte[length];
                Encoder e = Encoding.UTF8.GetEncoder();
                e.GetBytes(charData, 
0, charData.Length, byData, 
0
true);
                
//
 Move file pointer to beginning of file.
                aFile.Seek(
0, SeekOrigin.Begin);
                aFile.Write(byData, 
0, length);
            }
            
catch (IOException ex)
            {
                Console.WriteLine(
"
An IO exception has been thrown!
");
                Console.WriteLine(ex.ToString());
                Console.ReadKey();
                
return;
            }

 

 

转载于:https://www.cnblogs.com/jhxk/articles/2629043.html

你可能感兴趣的文章
Git提交代码报错Git push error:src refspec XXX matches more than one解决方案
查看>>
软件设计规格说明书
查看>>
bzoj 1500: [NOI2005]维修数列 -- splay
查看>>
设计模式 - 简单工厂
查看>>
数组与指针杂记
查看>>
四色原理
查看>>
Codeforces Round#500 Div.2 翻车记
查看>>
再更新ww的mingw MinGW-full-20101119
查看>>
Benefit UVA - 11889
查看>>
全排列 最详细的解题报告
查看>>
c++ web服务器
查看>>
android机型排行榜(201509)
查看>>
eclipse + maven + scala+spark环境搭建
查看>>
jmeter中webdriver插件,进行自动化压测
查看>>
整站开发初始化
查看>>
洛谷P2900 [USACO08MAR]土地征用Land Acquisition(斜率优化)
查看>>
uoj#448. 【集训队作业2018】人类的本质(Min_25筛+拉格朗日插值)
查看>>
vim配置及插件安装管理(超级详细)
查看>>
楼市仅是阶段性回暖 去库存仍是明年楼市主基调
查看>>
UIImagePickerController
查看>>