5

我有一个string[]包含代码。每行包含一些前导空格。我需要在不更改现有格式的情况下尽可能“取消缩进”代码。

例如 my 的内容string[]可能是

                                         公共课 MyClass
                                         {
                                             私有 bool MyMethod(string s)
                                             {
                                                 返回 s == "";
                                             }
                                         }

我想找到一种相当优雅和有效的方法(LINQ?)将其转换为

公共课 MyClass
{
    私有 bool MyMethod(string s)
    {
        返回 s == "";
    }
}

要清楚我正在寻找

IEnumerable<string> UnindentAsMuchAsPossible(string[] content)
{
    return ???;
}
4

6 回答 6

4

基于 Tim Schmelter 的回答:

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)));
}

这处理:

  • 制表符
  • 包含空行的源代码
于 2013-05-23T16:03:05.790 回答
3

这应该有效:

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

它将代码向左移动,所有行的空格数都相同。

例如:

string testString = @"       
                     public class MyClass
                     {
                         private bool MyMethod(string s)
                         {
                             return s == "";
                         }
                     }";


string[] lines = testString.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
string[] unindentedArray = UnindentAsMuchAsPossible(lines).ToArray();
于 2013-05-23T15:45:23.447 回答
3

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

IEnumerable<string> UnindentAsMuchAsPossible(string[] content)
{
    int spacesOnFirstLine = content[0].TakeWhile(c => c == ' ').Count();
    return content.Select(line => line.Substring(spacesOnFirstLine));
}
于 2013-05-23T15:34:17.023 回答
2

使用一点 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 );

印刷:

public class MyClass
{
    private bool MyMethod(string s)
    {
        return 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):

public class MyClass
{
        private bool MyMethod(string s)
        {
                return s == "";
        }
}

您可能需要修改此代码以处理边缘情况,例如零长度行或仅包含空格的行。

于 2013-05-23T15:36:46.970 回答
1

要匹配您想要的方法接口:

IEnumerable<string> UnindentAsMuchAsPossible(string[] content)
{
  int minIndent = content.Select(s => s.TakeWhile(c => c == ' ').Count()).Min();
  return content.Select(s => s.Substring(minIndent)).AsEnumerable();
}

这将获得所有行的最小缩进(仅假设空格,没有制表符),然后minIndent从每行的开头去除空格并将其返回为IEnumerable.

于 2013-05-23T15:48:04.753 回答
1

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

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变量使代码更具可读性。

于 2013-05-23T15:33:55.067 回答