日期:2014-05-19  浏览次数:20844 次

请教存储文件的算法-(100)
我有一个文本文件,格式为UTF-8(中英文都有),有452185行,每一行的文字字数不一样,总大小为11239Kb,现在想通过行号定位那一行的文字,刚开始我将其转换成GB-2312并用定长的string(非托管)去保存一行-> byte[],以二进制写入文件,读取的时候很方便,也不占太多内存,但是文件很大,是原文件的3倍左右;现在我分成两个文件,一个索引,一个内容,内容为每行的文字-> byte[],依次写成一个文件,大小为7500Kb,索引则是,以两个int32保存一行的文件信息,第一个int32保存一行文字的起始地址,第二个int32保存长度,索引文件大小为3533Kb,两个文件加起来有10.7M,这样和原文件差不多大,但是我都还觉得太大了,我考虑将索引文件的第二个int32改成int16,这样是治标不治本,我用RAR将两个文件压缩一下,只有4402Kb,相差太大了只有我的一半还少点.各位有没有好一点的算法,教教我吧,我实在是想不出来了,我本来想用GZipStream这个类来压缩,还是不行,他是要整体操作的.哎...

------解决方案--------------------
你的问题描述复杂,需求却让人看的很辛苦。作为程序员要学会抽象问题。
需求:
  1. 对一个带换行符  "\n " 的string 进行行号索引(随便内容是txt格式还是word格式你不care,你care用行号找到对应行的内容)
  2. 索引文件的格式任意,2进值流也可以,但要尽量小。
解决方案:
  1.使用换行符作为切分标志切分原string到string[]
2.准备一个压缩算法,GZipStream也可以,你把一行看作一个整体压就可以了。
  3.针对string[]内的每一个string(实际上是每一行压缩)把压缩后的文件和并在一个2进制流中。注意做这部的时候你拿个索引文件要跟踪每一行,记录下行数和压缩后需要合并到一起的字节数,这样使用的时候你就可以用byte[]去拿索引标示了。
  4.注意调用的时候需要反压缩行

  索引文件结构  行号,起始字节,字节长度
------解决方案--------------------
BTREE

------解决方案--------------------
摘录:《程序员秘书》--网络--纯真IP转换
编写其实很轻松,详见:http://www.psec.net.cn

这个实例比较长,给你一个思路:
1、你的要求和纯真IP数据库(数据量大,有40万行左右)的处理方法相同。网上讨论比较多。
2、纯真IP数据库处理数据比较好,40万行左右的数据才5M左右,程序员秘书经过改良后,生成的文本数据库文件还要小,查询时的速度还要快。
3、对这类数据库,《程序员秘书》处理思路如下(IP数据为例):
  1)文件分三个部份,IP段开始位置的位置段、IP段数据的具体内容、IP段(相当于索引)
  2)“IP段”的第一个位置写在文件的开始,就是“IP段开始位置的位置段”
  3)“IP段数据的具体内容”只记录不相同的内容。
  4)“IP段”记录开始和结束的IP段(如:1.0.0.0--1.3.0.0),同时记录具体内容在文本数据库中的位置。

------解决方案--------------------
恩...

不是很明白为什么楼主要用两个int32记录一行.

在我看来一个int32就够了,每个int32记录行的起始位置,当然这行长度就是两个紧挨着的int32相减.

内容还是到原文件中读取.
------解决方案--------------------
在压缩文件时,记录每行文本的位置(起始,结束)
在读取压缩文件时,按记录读取数据
Dim reader As New IO.StreamReader(New IO.FileStream( "test.txt ", IO.FileMode.Open), System.Text.Encoding.Default)
Dim IndexString As String
Dim zip As New ICSharpCode.SharpZipLib.Zip.ZipOutputStream(New IO.FileStream( "test.rar ", IO.FileMode.Create))
Dim temp As String
Dim bint As Integer
Dim eint As Integer
zip.PutNextEntry(New ICSharpCode.SharpZipLib.Zip.ZipEntry( "test "))
While reader.Peek <> -1
IndexString &= bint & ", "
temp = reader.ReadLine() + vbCrLf
Dim bytes() As Byte = System.Text.Encoding.Default.GetBytes(temp)
If bytes.Length > 0 Then
zip.Write(bytes, 0, bytes.Length)
eint += bytes.Length - 1
bint = eint + 1
IndexString &= eint & "; "
End If
End While
reader.Close()
zip.Close()
Dim indexs() As String = IndexString.Split(New Char() { "; ", ", "})
Dim uzip As New ICSharpCode.SharpZipLib.Zip.ZipInputStream(New IO.FileStream( "test.rar ", IO.FileMode.Open, IO.FileAccess.Read))
Dim theEntry As ICSharpCode.SharpZipLib.Zip.ZipEntry = uzip.GetNextEntry()
Dim stream As New IO.MemoryStream
Dim txt As IO.StreamReader
For i As Integer = 0 To indexs.Length - 2 Step 2
Dim bytes(indexs(i + 1) + 1 - indexs(i)) As Byte
uzip.Read(bytes, 0, bytes.Length)
stream.Write(bytes, 0, bytes.Length)
Next
stream.Position = 0
MsgBox(System.Text.Encoding.Default.GetString(stream.ToArray()))