C#多行字符串的缩进处理方法

4
我希望您能从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的答案。

我写了一个小扩展程序,使对齐方式“动态”:

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

返回正确的HTML代码:
<div class="myClassName">
    <span>Texte</span>
</div>

其中一个限制是它只能使用空格缩进...

欢迎任何改进!


考虑使用对象模型构建HTML,并让对象模型为您执行字符串转换。 - Eric Lippert
1
@EricLippert 我的示例展示了一些HTML,但我正在动态编写不同格式的代码,如cshtml视图、cs和js类等。我希望使用相对清晰的C#字符串插值语法来排序我的输出代码框架。我认为我的要求非常具体,必须为每种输出语言使用自定义模型...但是时间不够,无法编写它们 :-) - aprovent
1
请查看(并点赞)我对Visual Studio IDE的更改建议:缩进多行逐字字符串 - Olivier Jacot-Descombes
1
@OlivierJacot-Descombes 点赞已完成。非常好的建议,希望能尽快实现... - aprovent
4个回答

3
你可以将字符串换行以获得所需的缩进效果:
    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的第一个字符串中的空格数量与您的缩进量相同。


1
使用a=a.TrimStart()代替(末尾没有额外的空格) - Tim Schmelter
1
它将删除所有的空格字符。这包括空格、换行和制表符。 - Tim Schmelter
不,没有。我稍微编辑了一下你的答案。如果你不同意可以撤销。 - Tim Schmelter
@TimSchmelter +Yotam 当您的C#代码没有缩进时,它运行得非常好,但如果它在命名空间的类方法中,它将破坏C#缩进并且影响可读性太大... - aprovent
@aprovent 添加了另一个可能的解决方案。 - Yotam Salmon
显示剩余4条评论

2

我更喜欢这个解决方案,但是怎么样:

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

这样可以减少一些混乱:

  • 将字符串内的"替换为',这样您就不需要转义引号了。(HTML中单引号似乎是合法的。
  • 然后您也可以使用常规的""字符串字面量而不是@""
  • 使用\n代替Environment.NewLine

请注意,字符串连接是由编译器在编译时执行的。(有关此主题的博客文章,请参见Eric Lippert的这篇这篇博客文章,他曾经在C#编译器上工作。)没有运行时性能惩罚。


1
@Rick:你能支持你的说法吗?C#编译器将执行字符串连接。最终你会在你的程序集中得到一个巨大的字符串字面量。如果有任何性能损失,那么它会发生在编译时。请参见https://dev59.com/2nVC5IYBdhLWcg3wcwwm。 - stakx - no longer contributing
1
@aprovent:你指的性能损失是什么?没有任何实际影响。请看我上面的评论。 - stakx - no longer contributing
@Rick:当时,你可能没有连接字符串字面量(本质上是编译时常量),这是编译器可以优化的,而是连接从某个地方读取的字符串。这两种情况并不直接可比较。我同意,在后一种情况下使用StringBuilder是更合适的选择。 - stakx - no longer contributing
看看这个 StackOverflow 的帖子 - 它支持了我的观点 https://dev59.com/JnI_5IYBdhLWcg3wFu_L#1532483,对于一个“大字符串”,时间是“6.51分钟 vs 11秒”。 - Percy
我的错 - 那么这里是 - 我刚刚运行了它作为一个测试 - 将循环改为50000,你会看到一个很大的差异 https://dev59.com/lnI-5IYBdhLWcg3w6tFR#1612819 - Percy
显示剩余4条评论

2

受 Kotlin 中 trimIndent() 的启发。

这段代码:

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

将产生一个字符串:

anything
  you
want

或者"\n任何\n你想要的\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;
}

0
如果它是一个长字符串,那么您可以将该字符串保存在文本文件中,并将其读入变量中,例如:
string text = File.ReadAllText(@"c:\file.txt", Encoding.UTF8);

这样,您可以使用文本编辑器以任何想要的方式进行格式化,而不会对代码的外观产生负面影响。

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


1
我会使用嵌入式资源而不是单独的文本文件。 - stakx - no longer contributing
这只是一个想法,但如果字符串包含一些变量(比如字符串插值),你需要在文本文件中使用标记并替换它们,或者切换到模板引擎... 这不是我想要的针对 3/4 行字符串。 - aprovent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接