C# 从分隔文件中删除特定列

C# 从分隔文件中删除特定列,c#,delimiter,C#,Delimiter,这些天我一直在处理一些大的分隔文本(~1GB)文件。看起来有点像下面 COlumn1 #COlumn2#COlumn3#COlumn4 COlumn1#COlumn2#COlumn3 #COlumn4 其中#是分隔符 如果某列无效,我可能必须将其从整个文本文件中删除。列3无效时的输出文件应如下所示 COlumn1 #COlumn2#COlumn4 COlumn1#COlumn2#COlumn4 我在SO中找不到这个的c版本。我有办法做到吗?请帮忙 编辑: 我自己找到的解决方案如下所示。是否

这些天我一直在处理一些大的分隔文本(~1GB)文件。看起来有点像下面

COlumn1 #COlumn2#COlumn3#COlumn4
COlumn1#COlumn2#COlumn3 #COlumn4
其中#是分隔符

如果某列无效,我可能必须将其从整个文本文件中删除。列3无效时的输出文件应如下所示

COlumn1 #COlumn2#COlumn4
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);
int垃圾=3;
string line=“COlumn1#COlumn2#COlumn3#COlumn4”;
int计数器=0;
int colcount=line.Split(新字符[]{'#'},StringSplitOptions.None);
string[]linearray=line.Split(新字符[]{'#'},StringSplitOptions.None);
List linelist=linearray.ToList();
linelist.RemoveAt(垃圾-1);
string finalline=string.Empty;
foreach(行列表中的字符串s)
{
计数器++;
芬那林+=s;
if(计数器
已编辑

这种方法可能会占用大量内存,正如您在本文中所读到的,建议如下:

如果需要对文件中的数据运行复杂的查询,正确的做法是将数据加载到数据库中,让DBMS负责数据检索和内存管理

为了避免内存消耗,您应该使用
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;
    }
  }
}
使用System.Collections.Generic;
使用System.IO;
使用系统文本;
命名空间控制台应用程序1
{
班级计划
{
静态void Main(字符串[]参数)
{
常量字符串fileName=“temp.txt”;
var results=FindInvalidColumns(文件名);
使用(var reader=File.OpenText(文件名))
{
而(!reader.EndOfStream)
{
var builder=新的StringBuilder();
var line=reader.ReadLine();
如果(line==null)继续;
var split=line.split(新[]{“#”},0);
对于(变量i=0;i
首先,您要做的是使用第3列的0长度字符串将该行重新写入文本文件。因此,正确写入后的行如下所示:

COlumun1#COlumn2##COlumn4
如您所见,COlumn2和COlumn4之间有两个分隔符。这是一个没有数据的单元格。(我所说的“cell”是指某一行的一列。)稍后,当其他进程使用Split函数读取此值时,它仍然会为第3列创建一个新值,但在Split生成的数组中,第3个位置将是一个空字符串:

String[] columns = stream_reader.ReadLine().Split('#');
int lengthOfThirdItem = columns[2].Length;  // for proof
//  lengthOfThirdItem = 0
这会将无效值减少为null,并将其保留回文本文件中

有关字符串的详细信息,请参阅

当文本文件同时打开以供读取时,无法写入文本文件内部的行。本文对它进行了一些()的讨论,但似乎提问者只是希望能够将行写到最后。您希望能够在内部的任何点上书写线条。我认为如果不以某种方式缓冲数据,这是不可能的

缓冲数据的最简单方法是首先将文件重命名为临时文件(使用file.CoMovepy()/)。然后使用临时文件作为数据源。只需打开临时文件,读取可能有损坏条目的数据,然后使用我上面描述的表示空列的方法将数据重新写入原始文件名。完成后,应删除临时文件

重要


删除临时文件可能会使您容易受到电源和数据瞬变(或软件“瞬变”)的影响。(即,中断部分进程的断电可能会使数据处于不可用状态。)因此,您可能还希望将临时文件留在驱动器上,作为紧急备份,以防出现问题。

从split获取阵列,然后在将其写回之前移除该元素。要删除数组中的元素,请查看-1几乎肯定会导致OOM异常。鉴于OP说他们有一个1GB的文件要处理。@Aron有其他方法来缓冲该文件吗?您正在将输出放入StringBuilder中。某人最终应该和原始文件一样大。再加上GC的不实用性和不断增长的“列表”,这应该可以很容易地在你的记忆中咀嚼。由于您在SB中的任何时候都不会倒退,因此您可以同样轻松地用StreamWriter替换字符串生成器。如果行中已经包含一个具有空条目的字段,这将中断,对吗?意味着在我的行中,空列不必是无效列(很抱歉,我没有提到我的有效行可以在列中有空值,如COlumn1##COlumn3#COlumn4)。我不会说它实际上会中断。这很难
String[] columns = stream_reader.ReadLine().Split('#');
int lengthOfThirdItem = columns[2].Length;  // for proof
//  lengthOfThirdItem = 0