【问题标题】:removing lines and replacing string in text file删除文本文件中的行并替换字符串
【发布时间】:2016-02-13 00:45:41
【问题描述】:

我有一个包含超过 500 万行的文本文件。我需要逐行运行并删除某些行并替换某个字符串。我在 C# 中编写了一些“有效”的代码,但它可能需要将近一天的时间才能完成,这似乎很疯狂,因为在 notepad++ 中进行搜索和替换可以在几分钟内完成。但是,我们需要将其自动化。

文件可以任意包含一行

"<-comment 1: (more text on the line here)"

"<-Another line (more text on the line here)"

我想删除以注释 1 或其他行开头的任何行...

还有一个字符串

<tag>&#x2014;</tag> 

我想用下划线代替。这应该只出现在以“LINK:”开头的行上

我目前的代码是:

static void Main()
{
    const Int32 BufferSize = 128;
    int count = 0;
    int count2 = 0;
    string filename = @"C:\test\test.txt";
    string output = @"C:\text\output.txt";
    string Startcomment = @"<-comment 1:";
    string Startmoretext= @"<-Another line";
    string othercit = @"LINK:";
    string sub = @"<tag>&#x2014;</tag>";
    string subrepalce = @"_";

    string line;

    using (var filestream = File.OpenRead(filename))
    {
        Console.WriteLine("Start time: " + DateTime.Now.ToString());
        using (var streamreader = new StreamReader(filestream, Encoding.UTF8, true, BufferSize))
        {
            File.WriteAllText(output, "Clean text file" + Environment.NewLine);                    
            while ((line = streamreader.ReadLine()) != null)
            {
                count++;
                if(count % 10000 == 0)
                {
                    Console.WriteLine("Batch complete: " + DateTime.Now.ToString());
                    Console.WriteLine(count);
                }

                if(!line.StartsWith(Startcomment) && !line.StartsWith(Startmoretext))
                {
                    count2++;
                    if (line.StartsWith(othercit))
                    {
                        line = line.Replace(sub, subrepalce);
                    }
                    File.AppendAllText(output, line + Environment.NewLine);
                }
            }                    

        }                
        Console.WriteLine(DateTime.Now.ToString());
        Console.WriteLine(count + " Lines processed");
        Console.WriteLine(count2 + " Lines written back");
        Console.WriteLine("Finished!!!!!!");
        Console.Read();
    }
}

运行时间不可行。

我想让它在一个正则表达式下运行,如果我们需要添加新的异常,我们可以在脚本之外维护一个配置文件,但似乎也可以永远运行。

static void Main()
{
    const Int32 BufferSize = 128;
    string filename = @"C:\test\test.txt";
    XmlDocument xdoc = new XmlDocument();
    xdoc.Load(@"C:\test\RegexConfig.xml");
    XmlElement xmlRoot = xdoc.DocumentElement;
    XmlNodeList xmlNodes = xmlRoot.SelectNodes("/root/line");
    int count = 0;
    string line;
    using (var filestream = File.OpenRead(filename))
    {
        Console.WriteLine(DateTime.Now.ToString());
        using (var streamreader = new StreamReader(filestream, Encoding.UTF8, true, BufferSize))
        {
            File.WriteAllText(@"C:\test\output.txt", "Clean file" + Environment.NewLine);
            while ((line = streamreader.ReadLine()) != null)
            {
                string output = line;
                foreach (XmlNode node in xmlNodes)
                {
                    string pattern = node["pattern"].InnerText;
                    string replacement = node["replacement"].InnerText;                           
                    Regex rgx = new Regex(pattern);
                    output = rgx.Replace(output, replacement);
                    rgx = null;
                }
                if (output.Length > 0)
                {
                    count++;
                    if (count % 10000 == 0)
                    {
                        Console.WriteLine(count);
                        Console.WriteLine(DateTime.Now.ToString());
                    }
                    File.AppendAllText(@"C:\test\test.txt", output + Environment.NewLine);
                }

            }

        }
        Console.WriteLine(DateTime.Now.ToString());
        Console.WriteLine("Finished!!!!!!");
        Console.Read();
    }
}

XML 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <line>
        <pattern><![CDATA[<-comment 1:.*]]></pattern>
        <replacement><![CDATA[]]></replacement>
    </line> 
    <line>
        <pattern><![CDATA[<-Another line.*]]></pattern>
        <replacement><![CDATA[]]></replacement>
    </line> 
    <line>
        <pattern><![CDATA[<tag>&#x2014;</tag>]]></pattern>
        <replacement>_</replacement>
    </line> 
</root>

应该如何做这样的事情才能最有效地工作?

【问题讨论】:

  • 与从文件中读取的方式类似,您应该保持输出文件打开并写入其流以允许操作系统缓冲输出。 File.AppendAllText 每次都会打开和关闭文件。
  • 另外,您可以在 foreach 循环之前编译您的正则表达式。
  • 谢谢 C,我应该用什么代替?谢谢 Wilktor 我先研究一下如何编译,这是我第一次尝试使用正则表达式
  • 您是否考虑过在 Notepad++ 中录制和播放宏。它可能不是完全自动化的,而是半自动化的,工作量最少。只是我的两分钱。

标签: c# regex filestream


【解决方案1】:

我认为@C.Evenhuis 部分推荐以下方法更有效...

using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
using (StreamWriter writer = new StreamWriter("C:\test\test.txt"))
{
    string line;
    while ((line = sr.ReadLine()) != null)
    {
        string output = line;
        foreach (XmlNode node in xmlNodes)
        {
             string pattern = node["pattern"].InnerText;
             string replacement = node["replacement"].InnerText;                           
             Regex rgx = new Regex(pattern);
             output = rgx.Replace(output, replacement);
             rgx = null;
        }
        if (output.Length > 0)
        {
             count++;
             if (count % 10000 == 0)
             {
                  Console.WriteLine(count);
                  Console.WriteLine(DateTime.Now.ToString());
             }
             writer.WriteLine(output);
         }
    }
    writer.Close();
}

【讨论】:

  • 非常感谢。它已在几秒钟内运行。现在只检查输出
  • 文档是 xml,有没有办法设置编码,所以任何内部文本,如 都被编码为十六进制格式。诸如此类的通用标签:msdn.microsoft.com/en-us/library/…
  • 如果我尝试逐行编码,它会编码 xml 元素而不是我想要保留的标签
  • 抱歉,我无法得到您所需要的。让我说清楚。您在问题中提到的文本文件是 XML 格式,您需要在此 XML 文件中编码内部文本。编码,例如,&amp;lt;sub&amp;gt;&amp;lt;sub&amp;gt;??或者你指的是 HEX 的其他任何东西?
  • 这样解释更好吗? stackoverflow.com/questions/35504912/…
【解决方案2】:

如果在内存中执行并并行应用会怎样?像这样的:

    const Int32 BufferSize = 128;
    int count = 0;
    int count2 = 0;
    string filename = @"C:\test\test.txt";
    string output = @"C:\text\output.txt";
    string Startcomment = @"<-comment 1:";
    string Startmoretext= @"<-Another line";
    string othercit = @"LINK:";
    string sub = @"<tag>&#x2014;</tag>";
    string subrepalce = @"_";


    string line;
    string[] fileText = File.ReadAllLines(filename);

        Console.WriteLine("Start time: " + DateTime.Now.ToString());
    Parallel.For(0, fileText.Length, i=>{

      if(!fileText[i].StartsWith(Startcomment) && !fileText[i].StartsWith(Startmoretext))
                {
                    count2++;
                    if (fileText[i].StartsWith(othercit))
                    {
                        fileText[i]= fileText[i].Replace(sub, subrepalce);
                    }
                    File.WriteAllLines(yourPath, fileText);
                }
            }                    

        }                
        Console.WriteLine(DateTime.Now.ToString());
        Console.WriteLine(count + " Lines processed");
        Console.WriteLine(count2 + " Lines written back");
        Console.WriteLine("Finished!!!!!!");
        Console.Read();
    });

在内存中执行可能会更快。但请确保您有足够的 RAM 来存储。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-17
    • 1970-01-01
    • 2017-10-19
    • 2013-09-04
    • 2023-03-14
    • 2016-06-22
    • 2016-08-22
    • 2020-05-27
    相关资源
    最近更新 更多