作用于 c# 多行字符串的缩进

Acting on the indentation of a c# multiline string

我想从 c# 写一些 Html(html 是一个例子,这可能是其他语言..)

例如:

    string div = @"<div class=""className"">
                      <span>Mon text</span>
                   </div>";

将产生:

<div class="className">
            <span>Mon text</span>
         </div>

从 Html 的角度来看,这不是很酷...

获得正确 HTML 缩进的唯一方法是像这样缩进 C# 代码:

            string div = @"<div class=""className"">
    <span>Mon text</span>
</div>";

我们得到了正确的缩进 Html:

<div class="className">
    <span>Mon text</span>
</div>

但是像这样缩进 C# 确实破坏了代码的可读性...

有没有办法在 C# 语言中对缩进进行操作?

如果没有,有人有比以下更好的提示吗:

string div = "<div class=\"className\">" + Environment.NewLine +
             "  <span>Mon text</span>" + Environment.NewLine +
             "</div>";

并且优于

var sbDiv = new StringBuilder();
sbDiv.AppendLine("<div class=\"className\">");
sbDiv.AppendLine("    <span>Mon text</span>");
sbDiv.AppendLine("</div>");

我使用的解决方案:

非常感谢@Yotam 的回答。

我写了一点扩展来对齐 "dynamic" :

    /// <summary>
    /// Align a multiline string from the indentation of its first line
    /// </summary>
    /// <remarks>The </remarks>
    /// <param name="source">The string to align</param>
    /// <returns></returns>
    public static string AlignFromFirstLine(this string source)
    {
        if (String.IsNullOrEmpty(source)) {
            return source;
        }

        if (!source.StartsWith(Environment.NewLine)) {
            throw new FormatException("String must start with a NewLine character.");
        }

        int indentationSize = source.Skip(Environment.NewLine.Length)
                                .TakeWhile(Char.IsWhiteSpace)
                                .Count();

        string indentationStr = new string(' ', indentationSize);
        return source.TrimStart().Replace($"\n{indentationStr}", "\n");
    }

那我就可以这样用了:

private string GetHtml(string className)
{
    return $@"
            <div class=""{className}"">
                <span>Texte</span>
            </div>".AlignFromFirstLine();
}

那个 return 正确 html :

<div class="myClassName">
    <span>Texte</span>
</div>

一个限制是它只能使用 space 缩进...

欢迎任何改进!

我更喜欢,但是怎么样:

string div = "<div class='className'>\n"
           + "    <span>Mon text</span>\n"
           + "</div>";

这消除了一些混乱:

  • 将字符串中的 " 替换为 ',这样您就不需要转义引号了。 (Single quotes in HTML appear to be legal.)
  • 您还可以使用常规 "" 字符串文字代替 @""
  • 使用 \n 而不是 Environment.NewLine

请注意,the string concatenation is performed during compilation, by the compiler. (See also this and this 博客 post 由 Eric Lippert 撰写,他之前从事 C# 编译器的工作。)没有运行时性能损失。

您可以将字符串换到下一行以获得所需的缩进:

    string div = 
@"
<div class=""className"">
    <span>Mon text</span>
</div>"
.TrimStart(); // to remove the additional new-line at the beginning

另一个不错的解决方案(缺点:取决于缩进级别!)

        string div = @"
        <div class=""className"">
        <span>Mon text</span>
        </div>".TrimStart().Replace("\n            ", "\n");

它只是删除了字符串中的缩进。确保 Replace 第一个字符串中的空格数与缩进的空格数相同。

如果它是一个长字符串,那么您始终可以将该字符串保存在文本文件中并将其读入您的变量,例如

string text = File.ReadAllText(@"c:\file.txt", Encoding.UTF8);

这样您就可以使用文本编辑器随意设置格式,而且不会对代码的外观产生负面影响。

如果您要即时更改部分字符串,那么 StringBuilder 是您的最佳选择。 - 或者如果您确实决定从文本文件中读取字符串,您可以在字符串中包含 {0} 元素,然后使用 string.format(text, "text1","text2", etc) 更改所需的部分。

受 Kotlin 中的 trimIndent() 启发。

此代码:

    var x = @"
       anything
         you
       want
    ".TrimIndent();

将产生一个字符串:


anything
  you
want

"\nanything\n you\nwant\n"

实施:

public static string TrimIndent(this string s)
{
    string[] lines = s.Split('\n');

    IEnumerable<int> firstNonWhitespaceIndices = lines
        .Skip(1)
        .Where(it => it.Trim().Length > 0)
        .Select(IndexOfFirstNonWhitespace);

    int firstNonWhitespaceIndex;

    if (firstNonWhitespaceIndices.Any()) firstNonWhitespaceIndex = firstNonWhitespaceIndices.Min();
    else firstNonWhitespaceIndex = -1;

    if (firstNonWhitespaceIndex == -1) return s;

    IEnumerable<string> unindentedLines = lines.Select(it => UnindentLine(it, firstNonWhitespaceIndex));
    return String.Join("\n", unindentedLines);
}

private static string UnindentLine(string line, int firstNonWhitespaceIndex)
{
    if (firstNonWhitespaceIndex < line.Length)
    {
        if (line.Substring(0, firstNonWhitespaceIndex).Trim().Length != 0)
        {
            return line;
        }

        return line.Substring(firstNonWhitespaceIndex, line.Length - firstNonWhitespaceIndex);
    }

    return line.Trim().Length == 0 ? "" : line;
}

private static int IndexOfFirstNonWhitespace(string s)
{
    char[] chars = s.ToCharArray();
    for (int i = 0; i < chars.Length; i++)
    {
        if (chars[i] != ' ' && chars[i] != '\t') return i;
    }

    return -1;
}