【问题标题】:Acting on the indentation of a c# multiline string作用于 c# 多行字符串的缩进
【发布时间】:2016-07-22 09:37:23
【问题描述】:

我想用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,并让对象模型为您转换为字符串。
  • @EricLippert 我的示例显示了一些 Html,但我正在编写动态不同的代码格式,例如 cshtml 视图、cs 和 js 类,...我希望使用相对清晰的 C# 字符串插值语法订购我的输出代码骨架。我认为我的要求非常具体,以至于我必须为每种输出语言使用自定义模型...但是缺少时间来编写它们:-)
  • 请查看(并支持)我对 Visual Studio IDE 的更改建议:Indent multi-line verbatim strings。
  • @OlivierJacot-Descombes 投票完成。很好的建议,我希望它会尽快实施...

标签: c# string multiline


【解决方案1】:

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

    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 的第一个字符串中的空格数与缩进的空格数相同。

【讨论】:

  • 改用a=a.TrimStart()(最后没有额外的)
  • @TimSchmelter 好主意,谢谢!顺便说一句,Trim() 函数在不提供参数时如何工作?因为它似乎在给它一个'\n'时它在一开始就错过了一些东西,但是没有参数它就像一个魅力!
  • 它将删除所有white-spaces 的字符。这包括空格、换行符和制表符。
  • @TimSchmelter 酷,看起来很有用!每天学习新东西;-)
  • @TimSchmelter 关于第 1 条评论的编辑,最后多了一个 '\n',所以Trim() 是最好的
【解决方案2】:

我更喜欢this solution,但是怎么样:

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。 (另见 this 和 this 由 Eric Lippert 撰写的关于该主题的博文,他曾从事 C# 编译器的工作。)没有运行时性能损失。

【讨论】:

  • @Rick:你能支持你的说法吗? C# 编译器将执行字符串连接。您最终会在程序集中得到一个单一的字符串文字。如果有任何性能损失,如果发生在编译时。见stackoverflow.com/questions/288794/…。
  • @aprovent:您指的是什么性能影响?没有什么重要的。请参阅我上面的评论。
  • @stakx 谢谢,我学到了一些东西!所以这种技术的唯一问题是+ "和\n"的重复
  • 我在连接 1000 个字符串的项目中使用了 + 连接。它跑得像条狗!我改用StringBuilder,性能提升很大——我没有数据支持这一点——只是从经验中知道。对于该示例,这两种方法之间没有区别,因为它很小,但正如我所说,使用长字符串会很明显。在您的示例中,正在发生的事情是每次使用 + 时都会创建一个新字符串,这是性能影响的来源。
  • @Rick:那时,您可能没有连接编译器可以优化的字符串文字(本质上是编译时间常量),而是从某个地方读取的字符串。这两种情况没有直接可比性。我同意在后一种情况下使用StringBuilder 是更合适的选择。
【解决方案3】:

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

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

通过这种方式,您可以使用文本编辑器随意格式化它,并且不会对代码的外观产生负面影响。

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

【讨论】:

  • 我会使用嵌入式资源而不是单独的文本文件。
  • 这是一个想法,但是如果字符串包含一些变量(如字符串插值),您需要在文本文件中使用标记并替换它们或切换到模板引擎...这不是我的想要一个 3/4 行的字符串
【解决方案4】:

灵感来自 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-23
    • 2016-02-05
    • 2011-01-31
    • 1970-01-01
    • 1970-01-01
    • 2019-02-04
    • 2020-04-14
    相关资源
    最近更新 更多