审查征集贴:http://www.cnblogs.com/BeginnerClassroom/archive/2010/07/30/1788649.html
附录征集贴:http://www.cnblogs.com/BeginnerClassroom/archive/2010/08/04/1792175.html
欢迎各位园友对本书的某一部分内容进行拓展,将以附录的形式附在书后。
要求:
- 紧紧围绕一两个中心展开;
- 逻辑清晰,行文流畅;
- 考虑到初学者的基础。
- 写作时间最好不要少于一星期。
我写东西都是写好以后先放在那里,过段时间再读,重新修改,如此反复几次,就基本上很流畅了。
(PS:会署名,但无稿费,因为本来就没多少,不够分的。当然如果发了大财,我会分给大家的。)
标题 | 作者 | 状态 |
关于RichTextBox修改字体大小的研究 | 李雨来 | 已完稿 |
委托和接口的区别 | 汤非凡 | 正在写 |
XML格式注释 | Capricornus | 正在写 |
接口的显式实现以及与抽象类的比较 | 顾磊 | 正在写 |
.NET版本变更表 | 张智鸣 | 正在写 |
字符编码 | 赵士敬 | 正在写 |
读取流时应注意的一个问题 | 黄志斌 | 正在写 |
正则表达式在EmEditor里的应用 | 柳永法 | 正在写 |
绘图缓存 | 待选 | |
异步读写操作 | 待选 | |
控件开发、自定义控件 | MingHao_Hu | 正在写 |
读取流时应注意的一个问题
(本文由黄志斌提供)
Stream 类是所有流的抽象基类,通过它及它的子类,使程序员不必了解操作系统和基础设备的具体细节,即可对流进行“读取”、“写入”、“查询”等操作。希望本文的例子能帮助你掌握流的用法。
下面来研究一下Stream 类及其派生类的读取数据的成员。
Stream.Read()
Stream.ReadBytes()
BinaryReader.Read()
BinaryReader.ReadBytes()
TextReader.Read()
TextReader.ReadBlock ()
Stream.Read方法用于从流中读取字节序列,并将流的当前位置提升相应的字节数。在 MSDN 中有这样一句话:“即使尚未到达流的末尾,该方法获取到的字节数仍可以能少于所请求的字节数。”现在我们写一个程序来验证这一点。
using System; using System.IO; using Skyiv.Util; namespace Skyiv.Ben.StreamTest { sealed class Program { static void Display(string msg, int n) { Console.WriteLine("{0,22}: {1,7:N0}", msg, n); } static void Main() { var bs = new byte[128 * 1024]; //131,072
var ftp = new FtpClient("ftp://ftp.hp.com", "anonymous", "ben@skyiv.com"); Stream stream = ftp.GetDownloadStream("pub/softpaq/allfiles.txt"); BinaryReader binaryReader = new BinaryReader(stream); TextReader textReader = new StreamReader(stream); int count1 = stream.Read(bs, 0, bs.Length); int count2 = stream.ReadBytes(bs.Length).Length; int count3 = binaryReader.Read(bs, 0, bs.Length); int count4 = binaryReader.ReadBytes(bs.Length).Length; int count5 = textReader.Read(buf, 0, buf.Length); int count6 = textReader.ReadBlock(buf, 0, buf.Length); Display("Expect", bs.Length); Display("Stream.Read", count1); Display("Stream.ReadBytes", count2); Display("BinaryReader.Read", count3); Display("BinaryReader.ReadBytes", count4); Display("TextReader.Read", count5); Display("TextReader.ReadBlock", count6); } } }
======
将这个程序运行三次的结果如下: Expect: 131,072 Stream.Read: 4,356 Stream.ReadBytes: 131,072 BinaryReader.Read: 2,904 BinaryReader.ReadBytes: 131,072 TextReader.Read: 123,812 TextReader.ReadBlock: 131,072 Expect: 131,072 Stream.Read: 4,356 Stream.ReadBytes: 131,072 BinaryReader.Read: 4,356 BinaryReader.ReadBytes: 131,072 TextReader.Read: 2,904 TextReader.ReadBlock: 131,072 Expect: 131,072 Stream.Read: 4,356 Stream.ReadBytes: 131,072 BinaryReader.Read: 2,904 BinaryReader.ReadBytes: 131,072 TextReader.Read: 5,808 TextReader.ReadBlock: 131,072
可见,Stream.Read()、BinaryReader.Read()和TextReader.Read()方法,在尚未到达流的末尾情况下,获取到的字节数仍可以能少于所请求的字节数,这种问题在处理网络流(如FTP)、设备流(如串口输入)等情况时经常发生,而Stream.ReadBytes()、BinaryReader.ReadBytes()、和TextReader.ReadBlock()方法则无此问题。
现在,我们通过 Reflector 来查看BinaryReader.Read()方法的源程序代码。
public virtual int Read(byte[] buffer, int index, int count) { if (buffer == null) { throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); } if (index < 0) { throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (count < 0) { throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if ((buffer.Length - index) < count) { throw new ArgumentException(Environment.GetResourceString( "Argument_InvalidOffLen")); } if (this.m_stream == null) { __Error.FileNotOpen(); } return this.m_stream.Read(buffer, index, count); }
======
最后一行的m_stream的类型为 Stream,可见,BinaryReader.Read()方法在做一些必要的检查后就是简单地调用Stream.Read()方法,所以它们具有相同的问题。
而 BinaryReader.ReadBytes方法的源程序代码如下:
public virtual byte[] ReadBytes(int count) { if (count < 0) { throw new ArgumentOutOfRangeException("count", Environment. GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (this.m_stream == null) { __Error.FileNotOpen(); } byte[] buffer = new byte[count]; int offset = 0; do { int num2 = this.m_stream.Read(buffer, offset, count); if (num2 == 0) { break; } offset += num2; count -= num2; } while (count > 0); if (offset != buffer.Length) { byte[] dst = new byte[offset]; Buffer.InternalBlockCopy(buffer, 0, dst, 0, offset); buffer = dst; } return buffer; }
======
从上述代码中可以看出,BinaryReader.ReadBytes 方法循环地调用 Stream.Read 方法,直到达到流的末尾,或者已经读取了请求的 个字节为止。也就是说,如果没有到达流的末尾,该方法就一定会返回所请求的字节。
Stream.ReadBytes()方法其实是我写的一个扩展方法,源程序代码如下:
using System; using System.IO; namespace Skyiv.Util { static class ExtensionMethods { public static byte[] ReadBytes(this Stream stream, int count) { if(count < 0) throw new ArgumentOutOfRangeException("count","??????????"); var bs = new byte[count]; var offset = 0; for (int n = -1; n != 0 && count > 0; count -= n, offset += n) n = stream.Read(bs, offset, count); if (offset != bs.Length) Array.Resize(ref bs, offset); return bs; } } }
======
测试程序中使用的 FtpClient 类是我编写的类,可以参见我的另一篇随笔“如何直接处理FTP服务器上的压缩文件”[①],其源程序代码如下:
using System; using System.IO; using System.Net; namespace Skyiv.Util { sealed class FtpClient { Uri uri; string userName; string password; public FtpClient(string uri, string userName, string password) { this.uri = new Uri(uri); this.userName = userName; this.password = password; } public Stream GetDownloadStream(string sourceFile) { Uri downloadUri = new Uri(uri, sourceFile); if (downloadUri.Scheme != Uri.UriSchemeFtp) throw new ArgumentException("URI is not an FTP site"); FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(downloadUri); ftpRequest.Credentials = new NetworkCredential(userName, password); ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile; return ((FtpWebResponse)ftpRequest.GetResponse()).GetResponseStream(); } } }
======
[①] 地址为:http://www.cnblogs.com/skyivben/archive/2005/09/17/238920.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?