从分隔文件中删除特定列

时间:2014-05-08 04:52:44

标签: c# delimiter

这些天我一直在使用一些大的分隔文本(~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);

2 个答案:

答案 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)。然后使用临时文件作为数据源。只需打开要读取可能包含损坏条目的数据的临时文件,然后使用上面描述的方法重新将数据写入原始文件名以表示空列。完成后,您应该删除临时文件。

重要

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