首页 > 解决方案 > 处理和删除文件中的行,如果发送 CancellationToken 则停止

问题描述

我必须处理包含路径和状态信息的文件的行,并为该行/路径启动上传操作。在某些情况下上传可能失败,所以我必须保留该行,在其他情况下,可以在上传成功时删除该行。主要问题是,一切都在后台运行,用户可以随时关闭软件。在这种情况下,我设置了一个 Cancellation-token 并且文件操作完成。到现在为止,文件总是很小。所以我将所有仍然需要的行复制到一个新文件中并替换了旧文件。简化代码:

bool valid=false;
bool cancelled=false;
using (StreamWriter sw = new StreamWriter(filename_tmp, false))
{
    try
    {
        using (StreamReader sr = new StreamReader(filename)) //alternative:
            // foreach (string line in File.ReadLines(filename))
        {
            // process each line of file
            while (sr.Peek() >= 0)
            {
                string line = sr.ReadLine();
                rowCnt++;
                //separate line into content:
                string[] content = line.Split(delimiter);
                if (canceltoken.IsCancellationRequested && (rowCnt > 3))
                {
                    cancelled = true;
                }
                else
                {
                    data_path = content[0];
                    //start upload:
                    valid = UploadData(data_path);
                }
                if( cancelled || valid==false)
                {
                     sw.WriteLine("{0},{1},{2},{3}", data_path, uploadCnt,
                         DateTime.Now.ToString(), errorMsg);
                }
            }
        }
    }       
}
File.Replace(filename_tmp, filename, filename_backup);  

现在我们遇到文件可能变得非常大的情况,我担心将所有内容复制到新文件中会花费太长时间。用户当前收到一条消息,表明仍有进程在运行,软件将在之后关闭。1-5 秒后,软件关闭。现在需要更长的时间,我不希望用户使用任务管理器来终止进程。处理行并在之后删除它的最佳方法是什么?我对文件有完全的控制权,因为我写了它。所以我可以自己定义格式和作者(例如StreamWritervs. BinaryWriter)。

我想到了两种可能的选择:

  1. 处理整个文件。为每一行设置一个状态标志(如 1=删除我,2=必须处理/保留)。处理完文件后,再次遍历行并复制所需的行。如果取消,请保留旧文件。

我很想做类似的事情:

var linesToKeep = File.ReadLines(fileName).Where(l => l.Contains("remove me") ==false);
File.WriteAllLines(tempFile, linesToKeep);

但这需要我写到同一行来更改状态。我不确定那是否有效。我可以使用 BinaryWriter 来覆盖“标志”,但是我不能使用上面的行并且需要再次遍历每一行。

  1. 使用 seek 从最后处理文件。如果我使用 aBinaryWriter我会确切地知道线条的长度,所以这不会是一个问题。在额外文件中写入需要再次处理的错误行。使用 .在最后处理的行“剪切”原始文件FileStream.SetLength。这将产生 2 个文件(原始文件包含未处理的行,第二个文件包含需要再次处理的行)。但我还不知道如何处理额外的文件。下次我开始时我可以先处理这个,但后来我可能会得到越来越多的文件,这似乎是错误的。

我不知何故被困在这里,我不知道如何进一步进行。任何提示将不胜感激。

标签: c#winformsfilestreamcancellation-tokenbinaryreader

解决方案


我不会在取消后写入(或标记)所有未处理的行,而是反转问题:定义所有作业(每行一个),将其写入“队列”并在上传完成后将其删除。

要保留您的队列,您可以使用LiteDb。它是一个小巧方便的 No​​SQL 文件数据库,因此您没有 OR Mapper 的开销。

逻辑可能如下所示:

  1. 使用您的内容和一些附加属性(URL,...)定义 UploadJob 类
  2. 将作业列表写入 LiteDB 集合。
  3. 迭代每个作业。
  4. 作业成功完成后,在 LiteDB 中删除此项目。

推荐阅读