从文件中删除评论

时间:2014-04-07 14:40:38

标签: c# linq

我有一个像这样的文本文件

/* 
This is a comment 
I a looking to delete it
*/
//CALCULATE;     
Language([Dim Currency].[Currency].&[4]) = 2057;     
Language([Dim Currency].[Currency].&[2]) = 2067;  

我试过这段代码

var newLines = oldLines.Select(line => new { 
                Line = line, 
                Words = line.Split("/*") 
            })
            .Where(lineInfo => !lineInfo.Words.Contains(wordToDelete))
            .Select(lineInfo => lineInfo.Line);
var newLines1 = oldLines.Select(line => new { 
            Line = line, 
            Words = line.Split("*/") 
        })
        .Where(lineInfo => !lineInfo.Words.Contains(wordToDelete))
        .Select(lineInfo => lineInfo.Line);

代码返回此

This is a comment 
I a looking to delete it
//CALCULATE;     
Language([Dim Currency].[Currency].&[4]) = 2057;     
Language([Dim Currency].[Currency].&[2]) = 2067;

如何修改我的LINQ以使结果看起来像这样(没有块注释):

   //CALCULATE;     
    Language([Dim Currency].[Currency].&[4]) = 2057;     
    Language([Dim Currency].[Currency].&[2]) = 2067;

1 个答案:

答案 0 :(得分:2)

这是Aggregate LINQ运算符的完美用例,因为您将字符串列表(将输入文件拆分为单独的行的结果)转换为单个字符串,输入文件没有注释块。通常,当您希望将列表缩减为单个值时,或者您希望将状态从序列的一个元素传递到下一个元素(例如,一个有用的状态)时,达到Aggregate携带我们的是“我们是否在评论栏中?”作为布尔值。

在下面的查询中,我做了一个简化的假设,即开始和结束注释将始终在他们自己的行上。如果不是这种情况,那么Aggregate的主体变得更复杂,但基本上是相同的(您需要添加代码来处理在“/ *”或“* /”上分割行的行为)。这是一个满足您需求的查询:

var inComment = false; // start off assuming we're not in a comment
// assume lines is some IEnumerable<string> representing the lines of your file,
// perhaps from a call to File.ReadAllLines(<file name>)
var result = 
    lines.Aggregate(new System.Text.StringBuilder(),
                    (builder, line) => {
                         if (!inComment)
                             // more code here if "/*" isn't on its own line
                             inComment = line.StartsWith("/*");

                         if (inComment)
                         {
                             // more code here if "*/" isn't on its own line
                             inComment &= !line.StartsWith("*/");
                             return builder;
                         }

                         if (!inComment) builder.AppendLine(line);

                         return builder;
                     }).ToString();

为了简化示例,我没有在Aggregate方法中包含“我们处于评论块中”状态,而是关闭变量inComment。通过将inComment的类型更改为Aggregate(而不是Tuple<Boolean StringBuilder>,就像在上面的查询中一样)并使用StringBuilder,可以删除Item1以上的结算而不是inCommentItem2而不是builder


编辑:我没有解释Aggregate方法的主体,这可能很有价值,特别是因为其他评论者使用正则表达式链接到SO问题。首先,你不能用一个正则表达式删除所有注释块,你必须使用正则表达式以及一些额外的逻辑;在linked post中,Regex.Replace方法提供了这个额外的逻辑。这是一个比这里要求更重的解决方案。相反,您需要一个具有两种状态的简单状态机:InComment和NotInComment。当您处于InComment状态时,检查您所在的评论是否以当前行结束,如果是,则转到NotInComment状态。当您处于NotInComment状态时,检查是否在当前行开始注释。如果是这样,那么你跳过该行并移动InComment状态。如果没有,则将该行添加到输出中。 InComment状态由if (inComment)块表示,NotInComment状态是其他所有状态。