【问题标题】:Comparing two text files and subtracting respective values比较两个文本文件并减去各自的值
【发布时间】:2013-12-30 10:56:02
【问题描述】:

我有 2 个文本文件 default.txt 和 current.txt。

default.txt:

ab_abcdefghi_EnInP005M3TSub.csv FMR: 0.0009 FNMR: 0.023809524 SCORE: -4  Conformity: True
ab_abcdefghi_EnInP025M3TSub.csv FMR: 0.0039 FNMR: 0 SCORE: -14  Conformity: True
ab_abcdefghi_EnInP050M3TSub.csv FMR: 0.01989 FNMR: 0 SCORE: -18  Conformity: True
ab_abcdefghi_EnInP075M3TSub.csv FMR: 0.0029 FNMR: 0 SCORE: -17  Conformity: True
ab_abcdefghi_EnInP090M3TSub.csv FMR: 0.0002 FNMR: 0 SCORE: -7  Conformity: True

current.txt 看起来像这样

ab_abcdefghi_EnUsP005M3TSub.csv FMR: 0.0041 FNMR: 0 SCORE: -14  Conformity: True
ab_abcdefghi_EnUsP025M3TSub.csv FMR: 0.00710000000000001 FNMR: 0 SCORE: -14  Conformity: True
ab_abcdefghi_EnUsP050M3TSub.csv FMR: 0.0287999999999999 FNMR: 0 SCORE: -21  Conformity: True
ab_abcdefghi_EnUsP090M3TSub.csv FMR: 0.0113 FNMR: 0 SCORE: -23  Conformity: True

我需要做的是从默认值(默认电流)中减去当前值。

例如:

FMR_DIFF = FMR(default) - FMR(test)
FNMR_DIFF = FNMR(default) - FNMR(test)
SCORE_DIFF = SCORE(default) - SCORE(test)

我需要将它输出到一个文本文件中,输出看起来像这样

O/P:

result:   005M3TSub FMR_DIFF: -0.0032 FNMR_DIFF: 0.023809524 SCORE_DIFF: 10

我正在尝试在 C# 中执行此操作。到目前为止,我已经尝试在两个文件中读取行。我能够比较它们。我无法理解我需要实现的逻辑。我对编程很陌生。任何帮助表示赞赏。

【问题讨论】:

  • 好吧,把它分解成单独的任务——想想你想如何解析每一行文本。 (你说 current.txt 大多数时候都有不同的结构——你是什么意思?)
  • @JonSkeet current.txt 肯定会有我上面提到的数据。但这并不总是该文件可能拥有的唯一数据。它可能有一些其他数据。基本上我需要在两个文件中识别 P005 值(如果两者都存在。P005 可能存在于一个文件中而不存在于另一个文件中)并减去它们。
  • 那么您首先需要弄清楚如何识别相关行。我们不知道“P005 值”是什么。

标签: c# file file-comparison


【解决方案1】:

这是一个有趣的问题。请检查解决方案。它没有得到适当的优化。

首先,我们创建一个简单的 FileStructure 类来表示字符串:

public class DefaultFileStructure
    {
        public string FileId;
        public decimal FMR;
        public decimal FNMR;
        public int Score;
        public bool Conformity;
    }

定义用于解析的常量键名。

private static string DEFAULT_KN = "tv_rocscores_DeDeP";
private static string TEST_KN    = "tv_rocscores_FrFrP";

现在,解析文件并将数据存储在列表结构中。

    private List<DefaultFileStructure> GetFileStructure(string filePath, string keyName)
            {
                List<DefaultFileStructure> _defaultFileStructure = new List<DefaultFileStructure>();

                if(!File.Exists(filePath))
                {
                    Console.WriteLine("Error in loading the file");               
                }else{
                    string[] readText = File.ReadAllLines(filePath);
                    foreach (string s in readText)
                    {
                        _defaultFileStructure.Add(ParseLine(s, keyName));                    
                    }
                }

                return _defaultFileStructure;
            }

private DefaultFileStructure ParseLine(string Line, string Keyname)
        {
            DefaultFileStructure _dFileStruc = new DefaultFileStructure();

            string[] groups = Line.Split(new[] { ' ', ' ' },StringSplitOptions.RemoveEmptyEntries);

            /* -- Format Strucure, if the log provide same format always..
               Can also implement Expando concepts of C# 5.0 ***
                0[tv_rocscores_DeDeP005M3TSub.csv]
                1[FMR:]
                2[0.0009]
                3[FNMR:]
                4[0.023809524]
                5[SCORE:]
                6[-4]
                7[Conformity:]
                8[True]
             */

            _dFileStruc.FileId = groups[0].Replace(Keyname, "");
            _dFileStruc.FMR = decimal.Parse(groups[2]);
            _dFileStruc.FNMR = decimal.Parse(groups[4]);
            _dFileStruc.Score = int.Parse(groups[6]);
            _dFileStruc.Conformity = bool.Parse(groups[8]);

            return _dFileStruc;
        }

根据您的问题匹配差异并获得定义的结果。

 public void getDiff(String FirstFile, string SecondFile, string ResultFile)
        {
            try
            {
                //check if file exits....
                if (!File.Exists(FirstFile)) { return; }
                if (!File.Exists(SecondFile)) { return; }

                //Keep the result String..
                StringBuilder ResultBuilder = new StringBuilder();

                //Get the List of default file.
                List<DefaultFileStructure> DefaultList = GetFileStructure(FirstFile, DEFAULT_KN);

                //Get the List of test file.
                List<DefaultFileStructure> TestList = GetFileStructure(SecondFile, TEST_KN);


                //Get the diff and save in StringBuilder.
                foreach (DefaultFileStructure defFile in DefaultList)
                {
                    bool checkALL = false;
                    foreach (DefaultFileStructure testFile in TestList)
                    {
                        //Compare the file for diff.
                        if (defFile.FileId == testFile.FileId)
                        {
                            checkALL = false;
                            ResultBuilder.AppendLine(String.Format("result: {0} FMR_DIFF: {1} FNMR_DIFF: {2} SCORE_DIFF: {3}", defFile.FileId, defFile.FMR - testFile.FMR, defFile.FNMR - testFile.FNMR, defFile.Score - testFile.Score));
                            break;
                        }
                        else
                        {
                            checkALL = true;                      
                        }                        
                    }
                    if (checkALL == true)
                    {
                        ResultBuilder.AppendLine(String.Format("result: {0} FMR_DIFF: {1} FNMR_DIFF: {2} SCORE_DIFF: {3}", defFile.FileId, "N/A", "N/A", "N/A"));

                    }
                }

                //File processing completed.
                using (StreamWriter outfile = new StreamWriter(ResultFile))
                {
                    outfile.Write(ResultBuilder.ToString());
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

调用以下方法。

 getDiff(@"I:\Default_DeDe_operational_points_verbose.txt",
         @"I:\FrFr_operational_points_verbose.txt", 
         @"I:\Result.txt");

谢谢, 阿吉特

【讨论】:

    【解决方案2】:

    为了比较这些值,您首先必须解析它们。您可以创建一个代表单行(假/非假)匹配率的类:

    public class MatchRateLine
    {
        public int LineNumber { get; set; }
    
        public decimal FMR { get; set; }
        public decimal FNMR { get; set; }
        public int Score { get; set; }
        public bool Conformity { get; set; }
    }
    

    然后在你的解析器中你可以有这样的方法:

    public List<MatchRateLine> ParseFile(string filename)
    {
        var result = new List<MatchRateLine>();
    
        using (var reader = new StreamReader(filename))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                result.Add(ParseLine(line));
            }
        }
    
        return result;
    }
    

    进行实际解析的一种方法是:

    public MatchRateLine ParseLine(string line)
    {
        var result = new MatchRateLine();
    
        int fmrPosition = line.IndexOf("FMR: ");
        int fmnrPosition = line.IndexOf("FMNR: ");
    
        string fmrValueString = line.Substring(fmrPosition, fmnrPosition - fmrPosition);
        decimal fmrValue;
        if (decimal.TryParse(fmrValueString, out fmrValue))
        {
            result.FMR = fmrValue;
        }
    
        // repeat for other values
    
        return result;
    }
    

    在解析器中,我将“A line's FMR value”定义为“'FMR:'和'FMNR:'之间的文本,解析为十进制”。您必须为要提取的每个值应用此逻辑。

    现在,当您有两个集合时,您可以遍历它们并比较它们的值等等:

    var defaultLines = Parser.ParseFile("default.txt");
    var currentLines = Parser.ParseFile("current.txt");
    

    您的实际问题似乎是您可能想要比较 defaultcurrent 中的特定行,但是您在识别属于彼此的行时遇到了麻烦。如第 5 行的 default 中的 ab_abcdefghi_EnInP090M3TSubcurrent 中的 ab_abcdefghi_EnUsP090M3TSub 所示,它位于第 4 行(注意 In/Us)。

    为此,您可以使用属性扩展 MatchRateLine 类,在其中存储文件名或其子字符串的含义,以便您可以通过此值在两个列表中找到唯一的行。

    您可以再次为此使用Substring() 方法,在ParseLine() 方法中:

    // Position:  0123456789012345678901234567890
    // Filename: "ab_abcdefghi_EnInP090M3TSub.csv"
    
    result.ReportCode = line.Substring(17, 6);
    

    这将导致生成的MatchRateLine 具有ReportCode 属性,其值为P090M3

    再次给出两个行列表:

    var p090m3DefaultLine = defaultLines.First(l => l.ReportCode == "P090M3");
    var p090m3CurrentLine = currentLines.First(l => l.ReportCode == "P090M3");
    
    var fmrDiff = p090m3DefaultLine.FMR - p090m3CurrentLine.FMR;
    

    请注意,这段代码对格式做了很多假设,当被解析的行与该格式不匹配时,可能会抛出异常。

    【讨论】:

    • 我可能需要一些时间来理解这一点。
    • @Matt 是的,对不起,我倾向于忽略问题的重要细节,例如 “我能够比较它们”和 “我对编程很陌生”,然后开始愉快地打字。无论如何检查我更新的答案,我已经添加了一些细节。我对实际问题的解释是否正确?我的意思是 - 您是否无法识别相同的行,您是否无法确定要输出哪些行,您是否无法写入文件?
    • 很抱歉给您添麻烦了。我只是发表评论,因为我不想粗鲁,也不想发表评论。我正在考虑这个问题。感谢您添加的详细信息。再次为额外的麻烦道歉。
    • 我想通了.. 我删除了我的评论 :)
    • 抱歉,花了这么长时间才接受答案。我研究了我自己的答案版本。非常感谢您的帮助。
    【解决方案3】:

    您必须指定哪些行必须在输出中:默认的每个“文件”.csv?每个来自当前?两者(如果 2 个文件之一中缺少一个,则输出仍必须包含此 csv)?

    一旦你知道了,你就可以实现你的逻辑了:

    • 创建一个类(例如名为 FileLine),其属性为 行,也就是说:一个字符串代表name(这个字符串的名称:CsvName),一个小数代表FMR(比如FmrValue),一个小数代表FNMR(FnmrValue),一个int代表SCORE(ScoreValue)
    • 为进程创建一个方法。它将:

    • 检查当前文件的结构:如果无效,停止进程

    • 创建一个名为 defaultLines 的新列表
    • 创建一个名为 currentLines 的新列表
    • 创建一个名为 processesLine 的字符串(将在以后的步骤中使用)
    • 读取默认文件:foreach 行,创建 FileLine,解析行并实现你的 FileLine 的属性,并将 fileLine 添加到列表中(defaultLines)
    • 读取当前文件:foreach 行,创建 FileLine,解析行并实现你的 FileLine 的属性,并将 fileLine 添加到列表中(currentLines)
    • 然后处理比较(见后文)

      public void comparisonGenerator() {

      // HERE: add currentFile check
      
      // Initialization
      List<FileLine> defaultLines = new List<FileLine>();
      List<FileLine> currentLines = new List<FileLine>();
      
      // HERE: add file reading to populate defaultLines and currentLines 
      
      
      // Comparison
      foreach(FileLine item in defaultLines)
      {
          // for the item with the same name (using Linq, you could do it easily):
          FileLine cLine = currentLines.Single(l => l.CsvName.Equals(item.CsvName));
          if(cLine != null)
          {
              processedLine = String.Format("result: {0} FMR_DIFF: {1} FNMR_DIFF: {2} SCORE_DIFF: {3}", item.CsvName, item.FmrValue - cLine.FmrValue, item.FnmrValue - cLine.FnmrValue, item.ScoreValue - cLine.ScoreValue);
              // HERE: add this line to future output
          }
      }
      
      // When all lines are processed, write the output to a file using FileStream
      

      }

    【讨论】:

    • 非常感谢您的回答。我使用了两个答案中的最佳答案,现在我有了答案。
    猜你喜欢
    • 2018-08-24
    • 2018-02-14
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多