这些天我一直在使用一些大的分隔文本(~1GB)文件。它看起来有点低于
COlumn1 #COlumn2#COlumn3#COlumn4
COlumn1#COlumn2#COlumn3 #COlumn4
其中#是分隔符。
如果列无效,我可能需要将其从整个文本文件中删除。第3列无效时的输出文件应如下所示。
COlumn1 #COlumn2#COlumn4
COlumn1#COlumn2#COlumn4
string line = "COlumn1# COlumn2 #COlumn3# COlumn4";
int junk =3;
int columncount = line.Split(new char[] { '#' }, StringSplitOptions.None).Count();
//remove the [junk-1]th '#' and the value till [junk]th '#'
//"COlumn1# COlumn2 # COlumn4"
我无法在SO中找到这个版本的c#。有没有办法可以做到这一点?请帮忙。
修改 我发现自己的解决方案就像下面的工作一样。有没有办法可以更好地修改它,以便缩小大文本文件可能产生的性能影响?
int junk = 3;
string line = "COlumn1#COlumn2#COlumn3#COlumn4";
int counter = 0;
int colcount = line.Split(new char[] { '#' }, StringSplitOptions.None).Length - 1;
string[] linearray = line.Split(new char[] { '#' }, StringSplitOptions.None);
List<string> linelist = linearray.ToList();
linelist.RemoveAt(junk - 1);
string finalline = string.Empty;
foreach (string s in linelist)
{
counter++;
finalline += s;
if (counter < colcount)
finalline += "#";
}
Console.WriteLine(finalline);
答案 0 :(得分:2)
<强> EDITED 强>
这种方法可能内存非常昂贵,因为您可以阅读此post,建议应该是:
如果您需要针对文件中的数据运行复杂查询,那么正确的做法是将数据加载到数据库并让DBMS负责数据检索和内存管理。 < / p>
为避免内存消耗,您应该使用StreamReader
逐行读取文件
这可能是您的任务的开始,错过了无效的匹配逻辑
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const string fileName = "temp.txt";
var results = FindInvalidColumns(fileName);
using (var reader = File.OpenText(fileName))
{
while (!reader.EndOfStream)
{
var builder = new StringBuilder();
var line = reader.ReadLine();
if (line == null) continue;
var split = line.Split(new[] { "#" }, 0);
for (var i = 0; i < split.Length; i++)
if (!results.Contains(i))
builder.Append(split[i]);
using (var fs = new FileStream("new.txt", FileMode.Append, FileAccess.Write))
using (var sw = new StreamWriter(fs))
{
sw.WriteLine(builder.ToString());
}
}
}
}
private static List<int> FindInvalidColumns(string fileName)
{
var invalidColumnIndexes = new List<int>();
using (var reader = File.OpenText(fileName))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line == null) continue;
var split = line.Split(new[] { "#" }, 0);
for (var i = 0; i < split.Length; i++)
{
if (IsInvalid(split[i]) && !invalidColumnIndexes.Contains(i))
invalidColumnIndexes.Add(i);
}
}
}
return invalidColumnIndexes;
}
private static bool IsInvalid(string s)
{
return false;
}
}
}
答案 1 :(得分:0)
首先,您要做的是使用0长度字符串为COlumn3将行重写为文本文件。因此,正确写入后的行将如下所示:
COlumun1#COlumn2##COlumn4
如您所见,COlumn2和COlumn4之间有两个分隔符。这是一个没有数据的单元格。 (“单元格”是指某一行的一列。)后来,当其他一些进程使用Split函数读取它时,它仍然会为第3列创建一个新值,但在Split生成的数组中,第三个位置是一个空字符串:
String[] columns = stream_reader.ReadLine().Split('#');
int lengthOfThirdItem = columns[2].Length; // for proof
// lengthOfThirdItem = 0
这会将无效值减少为null并将它们保留在文本文件中。
有关String.Split的更多信息,请参阅C# StreamReader save to Array with separator。
当文本文件也打开以供读取时,无法写入文本文件内部的行。本文讨论了一些(simultaneous read-write a file in C#),但看起来问题提问者只是想能够在最后写行。您希望能够在内部的任何位置编写线条。我认为如果不以某种方式缓冲数据,这是不可能的。
缓冲数据的最简单方法是首先将文件重命名为临时文件(使用File.CoMovepy()// http://msdn.microsoft.com/en-us/library/system.io.file.move(v=vs.110).aspx)。然后使用临时文件作为数据源。只需打开要读取可能包含损坏条目的数据的临时文件,然后使用上面描述的方法重新将数据写入原始文件名以表示空列。完成后,您应该删除临时文件。
重要强>
删除临时文件可能会使您容易受到电源和数据瞬变(或软件“瞬态”)的影响。 (即,中断部分进程的掉电可能会使数据处于不可用状态。)因此,如果出现问题,您可能还希望将临时文件作为紧急备份保留在驱动器上。