C# 取消存储在字符串中的代码行的有效方法

C# 取消存储在字符串中的代码行的有效方法,c#,string,linq,C#,String,Linq,我有一个字符串[],其中包含代码。每行包含一些前导空格。我需要在不改变现有格式的情况下尽可能地“取消”代码 例如,我的字符串[]的内容可能是 public class MyClass { private bool MyMethod(string s)

我有一个
字符串[]
,其中包含代码。每行包含一些前导空格。我需要在不改变现有格式的情况下尽可能地“取消”代码

例如,我的
字符串[]
的内容可能是

public class MyClass { private bool MyMethod(string s) { return s == ""; } } 公共类MyClass { 私有布尔MyMethod(字符串s) { 返回s==“”; } } 我想找到一种相当优雅且高效的方法(LINQ?)将其转换为

public class MyClass { private bool MyMethod(string s) { return s == ""; } } 公共类MyClass { 私有布尔MyMethod(字符串s) { 返回s==“”; } } 说清楚我在找什么

IEnumerable<string> UnindentAsMuchAsPossible(string[] content)
{
    return ???;
}
IEnumerable unindentasmuchaspossable(字符串[]内容)
{
返回???;
}

这将首先找到最小标识,然后删除每行的空格数

var code = new [] { "  foo", "   bar" };

var minIndent = code.Select(line => line.TakeWhile(ch => ch == ' ').Count()).Min();
var formatted = code.Select(line => line.Remove(0, minIndent));

可以在一个表达式中编写所有内容,但尽管它在功能上更加优雅,但我认为
minIndent
变量使代码更具可读性。

只需计算第一行的前导空格数,然后从每行开头“删除”那么多字符:

IEnumerable<string> UnindentAsMuchAsPossible(string[] content)
{
    int spacesOnFirstLine = content[0].TakeWhile(c => c == ' ').Count();
    return content.Select(line => line.Substring(spacesOnFirstLine));
}
IEnumerable unindentasmuchaspossable(字符串[]内容)
{
int spacesOnFirstLine=content[0].TakeWhile(c=>c=='').Count();
返回content.Select(line=>line.Substring(spacesOnFirstLine));
}

使用一点LINQ和Regex查找最短缩进,然后从所有行中删除该数量的字符

string[] l_lines = { 
                        "                                         public class MyClass",
                        "                                         {",
                        "                                             private bool MyMethod(string s)",
                        "                                             {",
                        "                                                 return s == \"\";",
                        "                                             }",
                        "                                         }"  
                   };

int l_smallestIndentation =
    l_lines.Min( s => Regex.Match( s, "^\\s*" ).Value.Length );

string[] l_result =
    l_lines.Select( s => s.Substring( l_smallestIndentation ) )
           .ToArray();

foreach ( string l_line in l_result )
    Console.WriteLine( l_line );
印刷品:

公共类MyClass
{
私有布尔MyMethod(字符串s)
{
返回s==“”;
}
}
此程序将扫描数组中的所有字符串。如果可以假设第一行缩进最少,则可以通过仅扫描第一行来提高性能:

int l_smallestIndentation =
    Regex.Match( l_lines[0], "^\\s*" ).Value.Length;
还要注意,这将把制表符(
“\t”
)作为单个字符处理。如果混合使用制表符和空格,则反转缩进可能会很棘手。最简单的处理方法是在运行上面的代码之前,用适当数量的空格(通常是4个,尽管个别应用程序可能会有很大差异)替换所有选项卡实例

还可以修改上面的代码,以增加选项卡的权重。在这一点上,正则表达式不再有多大用处

string[] l_lines = { 
        "\t\t\tpublic class MyClass",
        "                        {",
        "                                private bool MyMethod(string s)",
        "                                {",
        "        \t        \t\treturn s == \"\";",
        "                                }",
        "\t\t\t}"  
    };

int l_tabWeight = 8;
int l_smallestIndentation =
    l_lines.Min
    (
        s => s.ToCharArray()
              .TakeWhile( c => Char.IsWhiteSpace( c ) )
              .Select( c => c == '\t' ? l_tabWeight : 1 )
              .Sum()
    );

string[] l_result =
    l_lines.Select
    (
        s =>
        {
            int l_whitespaceToRemove = l_smallestIndentation;
            while ( l_whitespaceToRemove > 0 )
            {
                l_whitespaceToRemove -= s[0] == '\t' ? l_tabWeight : 1;
                s = s.Substring( 1 );
            }
            return s;
        }
    ).ToArray();
打印(假设控制台窗口的选项卡宽度与我的一样为8):

公共类MyClass
{
私有布尔MyMethod(字符串s)
{
返回s==“”;
}
}
您可能需要修改此代码以处理边缘大小写场景,例如零长度行或仅包含空格的行。

这应该可以:

static IEnumerable<string> UnindentAsMuchAsPossible(IEnumerable<string> input)
{
    int minDistance = input.Min(l => l.TakeWhile(Char.IsWhiteSpace).Count());
    return input.Select(l => l.Substring(minDistance));
}

要匹配所需的方法界面,请执行以下操作:

IEnumerable<string> UnindentAsMuchAsPossible(string[] content)
{
  int minIndent = content.Select(s => s.TakeWhile(c => c == ' ').Count()).Min();
  return content.Select(s => s.Substring(minIndent)).AsEnumerable();
}
IEnumerable unindentasmuchaspossable(字符串[]内容)
{
int minIndent=content.Select(s=>s.TakeWhile(c=>c=='').Count()).Min();
返回内容。选择(s=>s.Substring(minIndent)).AsEnumerable();
}

这将获取所有行的最小缩进(仅假设为空格,不使用制表符),然后从每行的开头去除
minIndent
空格,并根据Tim Schmelter的回答返回
IEnumerable

static IEnumerable<string> UnindentAsMuchAsPossible(IEnumerable<string> lines, int tabWidth = 4)
{
    if (!lines.Any())
    {
        return Enumerable.Empty<string>();
    }

    var minDistance = lines
        .Where(line => line.Length > 0)
        .Min(line => line
            .TakeWhile(Char.IsWhiteSpace)
            .Sum(c => c == '\t' ? tabWidth : 1));
    var spaces = new string(' ', tabWidth);
    return input
        .Select(line => line.Replace("\t", spaces))
        .Select(line => line.Substring(Math.Min(line.Length, minDistance)));
}
静态IEnumerable UndentAsmuch可能(IEnumerable行,int tabWidth=4)
{
如果(!line.Any())
{
返回可枚举的.Empty();
}
var minDistance=线路
.Where(line=>line.Length>0)
.Min(行=>line
.TakeWhile(Char.IsWhiteSpace)
.Sum(c=>c='\t'?tabWidth:1));
变量空格=新字符串(“”,tabWidth);
返回输入
.Select(line=>line.Replace(“\t”,空格))
.Select(line=>line.Substring(Math.Min(line.Length,minDistance));
}
这将处理:

  • 制表符
  • 包含空行的源代码

你确定所有的都是空格,而不是任何标签吗?你不想让
不相关的
尽可能“返回”
void
,是吗?你是对的@Tim。修正。这假设第一行缩进最少(可能不是这样)。@MattHouser如果程序的格式正确,怎么可能不是这样?你有这样一个程序的例子吗?在最初的问题中,它在哪里说源代码的格式正确?问题只说明要维护现有的格式。此外,问题中的任何地方都没有说明代码是一个完整的程序。它可能只是一个函数的结尾(因此没有开始大括号,但有结束大括号)。如果你可以假设源代码是完整的,并且格式正确,那么你可以使用你的捷径。这正是我所想的。根据OP的需要,您还可以通过将:
l.TakeWhile(Char.IsWhiteSpace).Count()
更改为:
l.TakeWhile(Char.IsWhiteSpace).Sum(c=>c=='\t'?TabWidth:1)
其中
TabWidth
类似于4。另一个改进是处理空行。通常,如果有人编写的代码有空行,空行只包含“\r\n”。这段代码无法处理这个问题。我的用例既有制表符,也有空行。这个答案更重要。
static IEnumerable<string> UnindentAsMuchAsPossible(IEnumerable<string> lines, int tabWidth = 4)
{
    if (!lines.Any())
    {
        return Enumerable.Empty<string>();
    }

    var minDistance = lines
        .Where(line => line.Length > 0)
        .Min(line => line
            .TakeWhile(Char.IsWhiteSpace)
            .Sum(c => c == '\t' ? tabWidth : 1));
    var spaces = new string(' ', tabWidth);
    return input
        .Select(line => line.Replace("\t", spaces))
        .Select(line => line.Substring(Math.Min(line.Length, minDistance)));
}