比较两个文本文件并减去相应的值

时间:2013-12-30 10:56:02

标签: c# file file-comparison

我有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

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

E.g:

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#中这样做。到目前为止,我已尝试在两个文件中读取行。我能够比较它们。我无法理解我需要实现的逻辑。我是编程新手。任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:1)

为了比较这些值,您首先必须解析它们。您可以创建一个表示单行(False / Non-False)MatchRates的类:

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的FMR值” “'FMR:'和'FMNR:'之间的文本,解析为十进制”。您必须为要提取的每个值应用此逻辑。

现在当你有两个集合时,你可以循环它们并比较它们的值和诸如此类的东西:

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

您的实际问题似乎是您可能希望比较defaultcurrent中的特定行,但您无法识别彼此属于的行。正如第5行ab_abcdefghi_EnInP090M3TSub中的defaultab_abcdefghi_EnUsP090M3TSub中的current所见,第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;

请注意,此代码对格式做了很多假设,并且在被解析的行与该格式不匹配时会抛出异常。

答案 1 :(得分:1)

这是一个有趣的问题。请检查解决方案。它未正确优化。

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

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 :(得分:0)

您必须指定输出中必须包含哪些行:默认情况下每个“文件”.csv?从现在开始?两者(如果在两个文件中的一个中缺少一个,输出必须仍然包含此csv)?

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

  • 使用a的属性创建一个类(例如,名为FileLine) line,也就是说:name的字符串(此字符串的名称:CsvName),FMR的小数(比如FmrValue),FNMR的小数(FnmrValue),SCORE的一个int(ScoreValue)
  • 为流程创建方法。它将:

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

  • 创建一个名为defaultLines
  • 的新列表
  • 创建一个名为currentLines
  • 的新列表
  • 创建一个名为processedLine的字符串(将在以后的步骤中使用)
  • 读取默认文件: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
    

    }