前日因为遇到了在一个文件的中间位置追加文本的问题,也看了一些文章,做了一些试验,对愚翁提出的3中文件追加方式有一些感悟:
第一种对于文件尾扩展的操作,内容长度不限;
-----------------------------------
这种问题比较容易解决,只要设置FileMode的时候增加Append标示即可.
Code如下:
FileStream fs = new FileStream("F:\\test.txt", FileMode.Append, FileAccess.Write);
string str = "\n中华人string nul民1共3和 国
mailto:#!@!/n";
fs.Write(Encoding.GetEncoding("GB2312").GetBytes(str), 0, Encoding.GetEncoding("GB2312").GetByteCount(str));
fs.Close();
fs.Dispose();
如果追加的内容中有汉字,那么就要使用正确的编码格式,否则就会出现乱码.
从图中可以看到,str的内容被追加到了文件的末尾.
第二种等字节的替换操作,位置不限;
----------------------------
要实现这种替换,就需要使用文件流的seek方法了.说明如下:
public override long Seek (
long offset,
SeekOrigin origin
)
offset
相对于 origin 的点,从此处开始查找。
origin
使用 SeekOrigin 类型的值,将开始位置、结束位置或当前位置指定为 origin 的参考点。
其中第二个参数可以有3个取值:
成员名称 说明
Begin 指定流的开头。
Current 指定流内的当前位置。
End 指定流的结尾。
比如:
fs.Seek(41, SeekOrigin.Begin);
将流内的位置定在从流的开头向后41个字节的地方.
举例说明:
using (FileStream fs = new FileStream("F:\\test.txt", FileMode.Open , FileAccess.Write, FileShare.None))
{
fs.Seek(41, SeekOrigin.Begin);
string str1 = "888888888888888888888888888888888888888\r\n";
fs.Write(Encoding.GetEncoding("GB2312").GetBytes(str1), 0, Encoding.GetEncoding("GB2312").GetByteCount(str1));
}
在seek语句后,流内的位置就定在了从流开头偏移41个字节的位置上,然后再写入str的内容,结果如图所示:
但是我们可以在图中看到第一行只有39个”1”,为什么偏移41个字节后的位置是第二行的第一个字符呢?
这是因为换行符的原因,我们可以用UE的HEX格式看一下就明白了:
从图中可以看到,每次换行的时候,都有2个字符”0x0D”和”0x0A”就是ASCII中的10和13了,也就是我们编码时候的”\r\n”了,所以要想达到替换第二行39个”2”的目的,必须将流内的位置定在从开始偏移41个字节的位置上(39个”1”再加上换行回车符,共41个字符).
如果我们想替换最后一行39个”8”的话,我们来计算一下,应该把位置偏移定为多少?
应该是7*(39+2)的位置上,当然这是从流开头偏移的字节数,如果我们用SeekOrigin.End指定从流结尾的地方进行偏移就方便了,最后一行有39个”8”和换行回车符,共41个字节,这样我们用fs.Seek(-41, SeekOrigin.End);就可以将位置定在第8行的开始位置了.
有的时候,我们不能立刻得到需要替换的地方的位置,这样,我们可以边查找边找,比如还是上面的那个文件,如果现在我需要将39个”5”替换成39个”0”,在程序中,我们可以每次读取一行数据(每次读取41个字节),进行比对,如果是39个”5”的话,就可以得到这一行的最后位置了,想要替换这一行的数据,我们还需要将这个位置向前偏移41个字节的长度,然后就可以write数据了
Code如下:
using (FileStream fs = new FileStream("F:\\test.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
int nRealRead = 0;
int sum = 0;
byte[] bBuffer = new byte[41];
do
{
// Read data
nRealRead = fs.Read(bBuffer, 0, 41);
sum += nRealRead;
// Output data
String str = Encoding.Default.GetString(bBuffer, 0, nRealRead);
//偷懒了,反正39个字符都一样:)
if (str.Substring(0, 4) == "5555")
{
fs.Seek(
-41, SeekOrigin.Current );
string str1 = "000000000000000000000000000000000000000\r\n";
fs.Write(Encoding.GetEncoding("GB2312").GetBytes(str1), 0, Encoding.GetEncoding("GB2312").GetByteCount(str1));
break;
}
} while (nRealRead == 41);
}
上面seek的时候,还可以这样写: fs.Seek(sum
-41, SeekOrigin.Begin);
因为每次读取字节的大小都累加在了sum里面了,其实sum的大小和SeekOrigin.Current指向的是一样的,再向前偏移一行的长度就成了sum-41了.
最后一种就是位置不固定,字节数不确定。
--------------------------------
至于这种,我觉得需要使用的可能性不大,因为你需要替换文件某一部分,你就要知道需要替换的文本特征,这样就可以通过一行一段的查找找到了,就可以确定要替换的位置.至于”字节数不确定”,我觉得愚翁指的是这种情况:按行替换,我需要替换这行的数据,但是替换后的数据长度和替换前的数据长度不一样.比如:
替换前的文本:
11111
22222
33333
现在需要将第二行的文本替换成6个4,如下:
11111
444444
33333
这种情况下,如果要单单使用覆盖的话,估计是实现不了的,因为多于的文本会覆盖其它的文本,我们做个简单的试验,还是用那个文本文件,code如下:
FileStream fs = new FileStream("F:\\test.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
int nRealRead = 0;
int sum = 0;
byte[] bBuffer = new byte[41];
do
{
// Read data
nRealRead = fs.Read(bBuffer, 0, 41);
sum += nRealRead;
// Output data
String str = Encoding.Default.GetString(bBuffer, 0, nRealRead);
//偷懒了,反正39个字符都一样:)
if (str.Substring(0, 4) == "5555")
{
fs.Seek(-41, SeekOrigin.Current);
string str1 = "00000000000000000000000000000000000000099\r\n";
fs1.Write(Encoding.GetEncoding("GB2312").GetBytes(str1), 0, Encoding.GetEncoding("GB2312").GetByteCount(str1));
break;
}
} while (nRealRead == 41);
fs.Close();
fs.Dispose();
这次替换的内容是39个0和2个9和换行回车符,长度为43,比原来41要多2个字符.
这样的话,替换后第5行变成了39个0和2个9和换行回车符,这个时候多出来的2个字符就覆盖了第6行的开头2个字符.
这也就是说,替换来替换取,多于的字符总是要覆盖其它原来的字符,…….
如果要解决这个问题,我只能想出一个比较笨的方法,就是边写边读,先将不想替换的文本以前的部分读出来再写入另一个文本,再写入替换的文本,最后再将后面的文本写入......