xingd.net

.net related techonology

导航

今天想写一个小工具从一个文件中提取一些文本,代码如下:
public static void Main(string[] args)
{
    StreamReader sr 
= new StreamReader(args[0], Encoding.Default, false1);
    sr.BaseStream.Seek(
0x00005298, SeekOrigin.Begin);

    
string line = null;

    
do
    
{
        line 
= sr.ReadLine();
        Console.WriteLine(line);
    }

    
while (sr.BaseStream.Position < 0x0000B0DA);        
    
    sr.Close();
}


但是当读取到0x0000AE98时就结束了。将代码修改如下,以输出sr.BaseStream.Position;

public static void Main(string[] args)
{
    StreamReader sr 
= new StreamReader(args[0], Encoding.Default);
    sr.BaseStream.Seek(
0x00005298, SeekOrigin.Begin);

    
string line = null;

    
do
    
{
        line 
= sr.ReadLine();
        Console.WriteLine(line 
+ sr.BaseStream.Position);
    }

    
while (sr.BaseStream.Position < 0x0000B0DA);        
    
    sr.Close();
}


发现Position是以2048为一个步幅增加的,因此明白是由于StreamReader采取了Buffer所致的。通过在.NET Framework SDK中查找发现,通过构造函数可以指定StreamReader的buffer size,但最小值是128,也就是,无法禁用StreamReader的buffer。

想到的解决方法有两个,都是首先用FileStream.Read将整个区间的数据读到一个buffer中,然后可
1)以这个buffer为基础构建一个MemoryStream,然后使用StreamReader读取。
或者2)调用Encoding.Default.GetString(),得到string,然后构建一个StringReader读取。

两种方式的代码分别如下:
1)使用MemoryStream和StreamReader

public static void Main(string[] args)
{
    FileStream fs 
= new FileStream(args[0], FileMode.Open);

    fs.Seek(
0x00005298, SeekOrigin.Begin);
    
byte[] buffer = new byte[0x0000B0DA - 0x00005298];
    fs.Read(buffer, 
0, buffer.Length);

    fs.Close();

    StreamReader sr 
= new StreamReader(new MemoryStream(buffer), Encoding.Default);

    
string line = sr.ReadLine();

    
while (line != null)
    
{
        Console.WriteLine(line);
        line 
= sr.ReadLine();
    }


    sr.Close();
}


2)使用Encoding.GetString和StringReader

public static void Main(string[] args)
{
    FileStream fs 
= new FileStream(args[0], FileMode.Open);

    fs.Seek(
0x00005298, SeekOrigin.Begin);
    
byte[] buffer = new byte[0x0000B0DA - 0x00005298];
    fs.Read(buffer, 
0, buffer.Length);

    fs.Close();

    StringReader sr 
= new StringReader(Encoding.Default.GetString(buffer));

    
string line = sr.ReadLine();

    
while (line != null)
    
{
        Console.WriteLine(line);
        line 
= sr.ReadLine();
    }


    sr.Close();
}


我测试过两种方法后,得出的结论是StringReader快一点点,非常小的一点点。也许数据量大一点的情况下,会有不同的结果吧。

内部实现上,StringReader是用的string内的char遍历找出行分隔符,然后调用substring得到子串,而StreamReader则针对缓冲区的大小建立字符数组缓冲区(char[]),然后每次调用Decoder.GetChars从byte[]获取字符串数组,然后通过遍历char数组找出分隔符,最后创建一个StringBuilder,调用其Append(char[],...)方法返回子串。