BinaryWriter和BinaryReader和MemoryStream类读写内存

C#的FileStream类提供了最原始的字节级上的文件读写功能,但我们习惯于对字符串操作,于是StreamWriter和 StreamReader类增强了FileStream,它让我们在字符串级别上操作文件,但有的时候我们还是需要在字节级上操作文件,却又不是一个字节 一个字节的操作,通常是2个、4个或8个字节这样操作,这便有了BinaryWriter和BinaryReader类,它们可以将一个字符或数字按指定 个数字节写入,也可以一次读取指定个数字节转为字符或数字。

1.BinaryWriter类

BinaryWriter类以二进制形式将基元类型写入流,并支持用特定的编码写入字符串。


常用的方法:

Close      关闭当前的BinaryWriter和基础流

Seek       设置当前流中的位置

Write      将值写入当前流


2.BinartReader类

BinartReader类用特定的编码将基元数据类型读作二进制值。


常用的方法:

Close         关闭当前阅读器及基础流

Read          从基础流中读取字符,并提升流的当前位置

ReadBytes     从当前流将count个字节读入字节数组,并使当前位置提升count个字节

ReadInt32     从当前流中读取4个字节有符号整数,并使流的当前位置提升4个字节

ReadString    从当前流读取一个字符串。字符串有长度前缀,一次7位地被编码为整数



下面看一个实例:

BinaryWriter 和 BinaryReader 类用于读取和写入数据,而不是字符串。

  1. using UnityEngine;
  2. using System;
  3. using System.Text;
  4. using System.IO;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. public class FileOperator : MonoBehaviour {
  8. // Use this for initialization
  9. void Start () {
  10. WriteFile ();
  11. ReadFile();
  12. }
  13. void ReadFile() // 读取文件
  14. {
  15. FileStream fs = new FileStream ("D:\\MemoryStreamTest.txt", FileMode.Open, FileAccess.Read);
  16. BinaryReader r = new BinaryReader (fs);
  17. //以二进制方式读取文件中的内容
  18. int i = r.ReadInt32 ();
  19. float f = r.ReadSingle ();
  20. double d = r.ReadDouble ();
  21. bool b = r.ReadBoolean ();
  22. string s = r.ReadString();
  23. Debug.Log (i);
  24. Debug.Log (f);
  25. Debug.Log (d);
  26. Debug.Log (b);
  27. Debug.Log (s);
  28. r.Close ();
  29. fs.Close ();
  30. }
  31. void WriteFile() // 写入文件
  32. {
  33. FileStream fs = new FileStream ("D:\\BinaryStreamTest.txt", FileMode.OpenOrCreate);
  34. BinaryWriter w = new BinaryWriter (fs);
  35. //以二进制方式向创建的文件中写入内容
  36. w.Write (666); // 整型
  37. w.Write (66.6f); // 浮点型
  38. w.Write (6.66); // double型
  39. w.Write(true); // 布尔型
  40. w.Write ("六六六"); // 字符串型
  41. w.Close ();
  42. fs.Close();
  43. }
  44. }

MemoryStream和BufferedStream都派生自基类Stream,因此它们有很多共同的属性和方法,但是每一个类都有自己独特的用法。这两个类都是实现对内存进行数据读写的功能,而不是对持久性存储器进行读写。

读写内存-MemoryStream类

MemoryStream类用于向内存而不是磁盘读写数据。MemoryStream封装以无符号字节数组形式存储的数据,该数组在创建MemoryStream对象时被初始化,或者该数组可创建为空数组。可在内存中直接访问这些封装的数据。内存流可降低应用程序中对临时缓冲区和临时文件的需要。

下表列出了MemoryStream类的重要方法:

1、Read():读取MemoryStream流对象,将值写入缓存区。

2、ReadByte():从MemoryStream流中读取一个字节。

3、Write():将值从缓存区写入MemoryStream流对象。

4、WriteByte():从缓存区写入MemoytStream流对象一个字节。

Read方法使用的语法如下:

mmstream.Read(byte[] buffer,offset,count) 

其中mmstream为MemoryStream类的一个流对象,3个参数中,buffer包含指定的字节数组,该数组中,从offset到(offset +count-1)之间的值由当前流中读取的字符替换。Offset是指Buffer中的字节偏移量,从此处开始读取。Count是指最多读取的字节数。Write()方法和Read()方法具有相同的参数类型。

MemoryStream类的使用实例:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO;
  6. namespace ConsoleApplication1
  7. {
  8. class Program
  9. {
  10. static void Main()
  11. {
  12. int count;
  13. byte[] byteArray;
  14. char[] charArray;
  15. UnicodeEncoding uniEncoding = new UnicodeEncoding();
  16. // Create the data to write to the stream.
  17. byte[] firstString = uniEncoding.GetBytes("一二三四五");
  18. byte[] secondString = uniEncoding.GetBytes("上山打老虎");
  19. using (MemoryStream memStream = new MemoryStream(100))
  20. {
  21. // Write the first string to the stream.
  22. memStream.Write(firstString, 0, firstString.Length);
  23. // Write the second string to the stream, byte by byte.
  24. count = 0;
  25. while (count < secondString.Length)
  26. {
  27. memStream.WriteByte(secondString[count++]);
  28. }
  29. // Write the stream properties to the console.
  30. Console.WriteLine("Capacity={0},Length={1},Position={2}\n", memStream.Capacity.ToString(), memStream.Length.ToString(), memStream.Position.ToString());
  31. // Set the position to the beginning of the stream.
  32. memStream.Seek(0, SeekOrigin.Begin);
  33. // Read the first 20 bytes from the stream.
  34. byteArray = new byte[memStream.Length];
  35. count = memStream.Read(byteArray, 0, 20);
  36. // Read the remaining bytes, byte by byte.
  37. while (count < memStream.Length)
  38. {
  39. byteArray[count++] = Convert.ToByte(memStream.ReadByte());
  40. }
  41. // Decode the byte array into a char array
  42. // and write it to the console.
  43. charArray = new char[uniEncoding.GetCharCount(byteArray, 0, count)];
  44. uniEncoding.GetDecoder().GetChars(byteArray, 0, count, charArray, 0);
  45. Console.WriteLine(charArray); Console.ReadKey();
  46. }
  47. }
  48. }
  49. }

在这个实例代码中使用了using关键字。注意:

using 关键字有两个主要用途:

1、作为指令,用于为命名空间创建别名或导入其他命名空间中定义的类型。

例如:

  1. using System; 

2、作为语句,用于定义一个范围,在此范围的末尾将释放对象。

  1. using(Connection conn=new Connection(connStr))  
  2. {  
  3. }  
  4. //使用using关键字可及时销毁对象 

MemoryStream.Capacity 属性 取得或设定配置给这个资料流的位元组数目。

MemoryStream.Position 属性 指定当前流的位置。

MemoryStream.Length 属性获取用字节表示的流长度。

SeekOrigin()是一个枚举类,作用设定流的一个参数。

SeekOrigin.Begin我得理解就是文件的最开始,“0”是偏移,表示跳过0个字节。写2就是跳过2个字节。

MemoryStream类通过字节读写数据。本例中定义了写入的字节数组,为了更好的说明Write和WriteByte的异同,在代码中声明了两个byte数组,其中一个数组写入时调用Write方法,通过指定该方法的三个参数实现如何写入。

另一个数组调用了WriteByte方法,每次写入一个字节,所以采用while循环来完成全部字节的写入。写入MemoryStream后,可以检索该流的容量,实际长度,当前流的位置,将这些值输出到控制台。通过观察结果,可以确定写入MemoryStream流是否成功。

调用Read和ReadByte两种方法读取MemoryStream流中的数据,并将其进行Unicode编码后输出到控制台。







读取内存流中的数据


在.NET中,使用抽象基类System.IO.Stream代表流,它提供Read和Write两个方法。由于数据流的有序性,因此流对象还有一个读写指针,为此,Stream类还有一个Seek方法用于移动读写指针。  


字符串与字节数组间的互相转化:

  1. string str = "内存大小";
  2. byte[] temp = Encoding.UTF8.GetBytes (str); // 字符串转化为字节数组
  3. string s = Encoding.UTF8.GetString (temp); // 字节数组转化为字符串
  4. Debug.Log (s);

Encoding用法比较简单,如果只是字节和字符的互相转换,GetBytes()和GetChars()这两个方法及它们的重载基本上会满足你所有要求。

GetByteCount()及其重载是得到一个字符串转换成字节时实际的字节个数。

GetCharCount()及其重载是得到一个字节数组转换成字符串的大小。

Decoder.GetChars 方法

Decoder.GetChars (Byte[], Int32, Int32, Char[], Int32)
在派生类中重写时,将指定字节数组的字节序列和内部缓冲区中的任何字节解码到指定的字符数组。
在派生类中重写时,将一个字节序列解码为一组字符。

Java里一个byte取值范围是-128~127, 而C#里一个byte是0~255.

首位不同. 但是底层I/O存储的数据是一样的

FileStream对象的数据来自文件,而MemoryStream对象的数据来自内存缓冲区。这两个类都继承自Stream类。 

MemoryStream的数据来自内存中的一块连续区域,这块区域称为“缓冲区(Buffer)”。可以把缓冲区看成一个数组,每个数组元素可以存放一个字节的数据。

  1. 在创建MemoryStream对象时,可以指定缓冲区的大小,并且可以在需要的时候更改。
  2. //字节数组
  3. byte[] buffer = new byte[600];
  4. //填充字节数组
  5. private void CreateExampleData()
  6. {
  7. for(int i=0; i<600; i++)
  8. {
  9. //byte类型的数最大不能超过255,用256取模实现
  10. buffer[i] = (byte)(i%256);
  11. }
  12. }
  13. 内存流的基本使用方法:
  14. private void OnTestMemory()
  15. {
  16. //创建测试数据
  17. CreateExampleData();
  18. //创建内存流对象,初始分配50字节的缓冲区
  19. MemoryStream mem = new MemoryStream(50);
  20. //向内存流中写入字节数组的所有数据
  21. mem.Write(buffer,0,buffer.GetLength(0));
  22. MessageBox.Show("写入数据后的内存流长度:" + mem.Length.ToString());
  23. MessageBox.Show("分配给内存流的缓冲区大小:" + mem.Capacity.ToString());
  24. mem.SetLength(550);
  25. MessageBox.Show("调用SetLength方法后的内存流长度:" + mem.Length.ToString());
  26. mem.Capacity = 620;//此值不能小于Length属性
  27. MessageBox.Show("调用Capacity方法后缓冲区大小:" + mem.Capacity.ToString());
  28. //将读写指针移到距流开头10个字节的位置
  29. mem.Seek(10,SeekOrigin.Begin);
  30. MessageBox.Show(mem.ReadByte().ToString());
  31. mem.Close();
  32. }
  33. 内存流的Length属性代表了其中存放的数据的真实长度,而Capacity属性则代表了分配给内存流的内存空间大小。
  34. 可以使用字节数组创建一个固定大小的MemoryStream,
  35. MemoryStream mem = new MemoryStream(buffer);
  36. 这时,无法再设置Capacity属性的大小。
  37. 还可以创建只读的内存流对象。
  38. MemoryStream mem = new MemoryStream(buffer,false);
  39. FlieStream用于存取文件。
  40. 创建文件并写入内容:
  41. //创建一个新文件
  42. FileStream fsForWrite = new FileStream("test.data",FileMode.Create);
  43. try
  44. {
  45. //写入一个字节
  46. fsForWrite.WriteByte(100);
  47. CreateExampleData();
  48. //将字节数组写入文件
  49. fsForWrite.Write(buffer,0,buffer.GetLength(0));
  50. }
  51. catch(Exception ex)
  52. {
  53. MessageBox.Show(ex.Message);
  54. }
  55. finally
  56. {
  57. //关闭文件
  58. fsForWrite.Close();
  59. }
  60. 打开文件并读取内容:
  61. private void ReadFromFile()
  62. {
  63. FileStream fsForRead = new FileStream("test.data",FileMode.Open);
  64. try
  65. {
  66. //读入一个字节
  67. MessageBox.Show("文件的第一个字节为:"+fsForRead.ReadByte().ToString());
  68. //读写指针移到距开头10个字节处
  69. fsForRead.Seek(10,SeekOrigin.Begin);
  70. byte[] bs = new byte[10];
  71. //从文件中读取10个字节放到数组bs中
  72. fsForRead.Read(bs,0,10);
  73. }
  74. catch(Exception ex)
  75. {
  76. MessageBox.Show(ex.Message);
  77. }
  78. finally
  79. {
  80. fsForRead.Close(); }
  81. }
  82. 如果一个程序退出了,但它打开的文件没有被关闭,将导致其他程序无法修改或删除此文件。

FileStream与MemoryStream间的相互作用:

-----解决方案--------------------
  FileStream fs = new FileStream(path, FileMode.Open); 
  byte[] data = new byte[fs.Length]; 
  fs.Read(data, 0, data.Length); 
  fs.Close();
  MemoryStream ms = new MemoryStream(data);

------解决方案--------------------

 ///定义并实例化一个内存流,以存放图片的字节数组。
MemoryStream m = new MemoryStream();
///获得当前路径
string strAppPath = AppDomain.CurrentDomain.BaseDirectory; //获得可执行文件的路径。
///获得图片路径 
string strPath = strAppPath + "img\\default.jpg"; 
///图片读入FileStream 
FileStream f = new FileStream(strPath, FileMode.open); 
///把FileStream写入MemoryStream 
m.SetLength(f.Length); 
f.Read(m.GetBuffer(), 0, (int)f.Length); 
m.Flush(); 
f.Close();

------解决方案--------------------
            FileStream fs = new FileStream(fileName, FileMode.Open);
            byte[] MyData = new byte[fs.Length];
            fs.Read(MyData, 0, (int)fs.Length);
            fs.Close();
            MemoryStream ms = new MemoryStream(MyData);

------解决方案--------------------
MemoryStream ms = new MemoryStream(File.ReadAllBytes("c:\\1.jpg"));
版权声明:本文为Game_jqd原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Game_jqd/article/details/81024283

智能推荐

26_Python基础_继承

面向对象三大特性: 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 继承 实现代码的重用, 相同的代码不需要重复的编写 多态 不同的对象调用相同的方法,  产生不同的执行结果,  增加代码的灵活度 1.  单继承 1.1 概念 继承的概念:&...

循环

与任何程序设计语言一样Java利用条件语句与循环结构确定流程控制,一下总结一下Java中的循环语句: while do while for switch 对于golang来说: switch非常灵活。从第一个expr为true的case开始执行,如果case带有fallthrough,程序会继续执行下一条case,不会再判断下一条case的expr,如果之后的case都有fallthrough,d...

1638 统计只差一个字符的子串数目(动态规划)

1. 问题描述: 给你两个字符串 s 和 t ,请你找出 s 中的非空子串的数目,这些子串满足替换一个不同字符以后,是 t 串的子串。换言之,请你找到 s 和 t 串中恰好只有一个字符不同的子字符串对的数目。比方说, "computer" 和 "computation"...

websocket基本原理

HTTP中一个request只能有一个response。而且这个response也是被动的,不能主动发起 因此过去的服务端推送信息是通过客户端不停的轮询实现的 websocket是双向通信协议,提供了服务端主动推送信息的能力 需要客户端(浏览器)和服务端同时支持 如果经过代理的话,还需要代理支持,否则有些代理在长时间无通信时会自动切断连接 因此WS为了保证连接不被断掉,会发心跳 WebSocket...

mybatis+ehcache二级缓存

导入jar包 mapper.xml文件开启二级缓存 pojo类实现序列化接口 配置ehcache.xml 测试...

猜你喜欢

python+opencv实现图像拼接

任务 拍摄两张图片去除相同部分,拼接在一起 原图 结果 步骤 读取两张图片 使用sift检测关键点及描述因子 匹配关键点 处理并保存关键点 得到变换矩阵 图像变换并拼接 代码实现 扩展 这里对右边图像进行变换,右边变得模糊,可以修改代码对左边图像变换 这里只有两张图片拼接,可以封装实现多张图片拼接 可以修改代码实现上下图片的拼接...

python_sklearn机器学习算法系列之AdaBoost------人脸识别(PCA,决策树)

          注:在读本文之前建议读一下之前的一片文章python_sklearn机器学习算法系列之PCA(主成分分析)------人脸识别(k-NearestNeighbor,KNN)         本文主要目的是通过一个简单的小...

memmove函数与memcpy函数的模拟实现

memmove函数和memcpy函数都是在内存复制任意类型的,但是它俩也有区别。当源区域和目标区域有重复的,memmove函数会复制缓冲区重叠的部分,而memcpy相反,会报出未知错误。 下面给出两个函数的实现 首先,memmove函数。 实现的基本原理如下图。 具体代码如下: memcpy函数的实现很简单,就直接给出源代码了...

SpringFramework核心 - IOC容器的实现 - 总结

1. 概述 把Spring技术内幕第一章和第二章过了一遍,也做了一些笔记, 对IOC容器的实现有了一定皮毛理解,现在跟着源码再过一遍总结一下IOC容器的初始化,Bean的初始化的过程,做一下总结 ① IOC容器和简单工厂模式 在开始之前,先想想我们平时是怎么使用IOC容器为我们管理Bean的,假设我们要把下面的User类交给IOC容器管理 我们不想关心如何创建一个User对象实例的,仅仅在需要他的...