作者:张梦蒙4428 | 来源:互联网 | 2023-09-15 15:14
问题:读取一个444M的文本文件,出现内存溢出方案1:利用FileStream读取byte,利用换行符次数为间隔区间,依次读取(边读边处理):优缺点:速度快,不适合读取指定行(
问题:读取一个444M的文本文件,出现内存溢出
方案1:利用FileStream读取byte,利用换行符次数为间隔区间,依次读取(边读边处理):
优缺点:速度快,不适合读取指定行(因为是用遍历的,所以读取指定行会比较慢)
解决思路:获取区间数组中最后一个换行符(\n的byte字节),以此为分界点,该换行符以后的数据,留给下一个区间来组装;
代码:
///
/// 通过给定的文件流,判断文件的编码类型
///
/// 文件流
/// 文件的编码类型
public Encoding GetType(Stream fs) {byte[] Unicode = new byte[] { 0xFF, 0xFE, 0x41 };byte[] UnicodeBIG = new byte[] { 0xFE, 0xFF, 0x00 };byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF }; //带BOMEncoding reVal = Encoding.Default;BinaryReader r = new BinaryReader(fs, System.Text.Encoding.Default);int i;int.TryParse(fs.Length.ToString(), out i);byte[] ss = r.ReadBytes(i);if (IsUTF8Bytes(ss) || (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF)) {reVal = Encoding.UTF8;} else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00) {reVal = Encoding.BigEndianUnicode;} else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41) {reVal = Encoding.Unicode;}r.Close();return reVal;
}///
/// 通过给定的文件流,判断文件的编码类型
///
/// 文件流
/// 文件的编码类型
public Encoding GetType2(byte[] ss)
{Encoding reVal = Encoding.Default;if ((ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF) || IsUTF8Bytes(ss)){reVal = Encoding.UTF8;}else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00){reVal = Encoding.BigEndianUnicode;}else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41){reVal = Encoding.Unicode;}return reVal;
}///
/// 获取文件编码
///
///
///
public Encoding GetEncoding(string filePath) {FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);Encoding r = GetType(fs);fs.Close();return r;
}///
/// FileStream读取文件文本
///
///
/// 间隔多少个换行符读取一次
///
public void FSLimitReadFileText(string filePath,int txtCount)
{if (!File.Exists(filePath)){return;}FileTool ft = new FileTool();byte rn = new UTF8Encoding(false).GetBytes("\n")[0];using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)){Encoding encoding = null;List bList = new List();string text;int rnCount = 0;int cnt, m;m = 0;cnt = fs.ReadByte();byte by;while (cnt != -1){if (!isRuning){break;}by = Convert.ToByte(cnt);bList.Add(by);if (by == rn){rnCount++;}if (rnCount >= txtCount){rnCount = 0;if (encoding == null){encoding = ft.GetType2(bList.ToArray());if (encoding is UTF8Encoding){encoding = new UTF8Encoding(false);}}text = encoding.GetString(bList.ToArray());//这里就可以执行你的任务了//...............//最后这里清空byte列表,否则下一次会数据会重复bList.Clear();}cnt = fs.ReadByte();}if (bList.Count > 0){if (encoding == null){encoding = ft.GetType2(bList.ToArray());if (encoding is UTF8Encoding){encoding = new UTF8Encoding(false);}}text = encoding.GetString(bList.ToArray());//这里就可以执行你的任务了//...............bList.Clear();}}}
方案2:利用IEnumerable进行迭代获取
优缺点:几乎不怎么占内存,速度遍历的话速度比较慢
如果是只需要取其中几行的话可以考虑用这个,如果是要全部读取,那么不推荐,
同一个444M的文件,利用IEnumerable迭代完毕需要4分多钟,方案一也就13秒钟
(这里我是需要将数据变成字符串,依次进行处理,所以会比较慢,如果是文件的粘贴复制之类的,建议用BufferStream)
public string ReadFileLineText(string filePath)
{if (!File.Exists(filePath)){return null;}int limit = 5000;//每次读取5000行string text = null;Encoding encoding = GetEncoding(filePath); //GetEncoding方法参考方案1if (encoding is UTF8Encoding){encoding = new UTF8Encoding(false);}IEnumerable lines = File.ReadLines(filePath, encoding);int size = lines.Count();//遍历// IEnumerable tempLines;// IEnumerator iter;// for (int i = 0; i = size ? size % limit : limit;// tempLines = lines.Skip(i).Take(max);// iter = tempLines.GetEnumerator();// while (iter.MoveNext())// {// string dataStr = iter.Current;// //SetLog(dataStr);// }// }// }//取第994400后面的5000行数据(不包含994400行)lines = lines.Skip(994400).Take(limit);IEnumerator iter = lines.GetEnumerator();while (iter.MoveNext()){string dataStr = iter.Current;SetLog(dataStr);}return text;
}