【问题标题】:multiline formatting for verbatim strings in c# (prefix with @)c#中逐字字符串的多行格式(带有@前缀)
【发布时间】:2011-11-02 22:28:21
【问题描述】:

我喜欢在 c# 中使用 @"strings",尤其是当我有很多多行文本时。唯一的烦恼是我的代码格式在这样做时会变得笨拙,因为第二行和更大的行被完全推到左边,而不是使用我格式精美的代码的缩进。我知道这是设计使然,但是是否有一些选项/黑客方法可以让这些行缩进,而不在输出中添加实际的制表符/空格?

添加示例:

        var MyString = @" this is 
a multi-line string
in c#.";

我的变量声明缩进到“正确”的深度,但字符串中的第二行和后面的行被推到左边距 - 所以代码有点难看。您可以在第 2 行和第 3 行的开头添加制表符,但字符串本身将包含这些制表符...有意义吗?

【问题讨论】:

  • 我通常做的是在自己的行上开始字符串(即@之前的换行符),所以至少它不在右边然后突然在左边。我知道这不是您想要的解决方案。
  • 是的,我也经常这样做。这真的不是什么大问题,但我认为这将是一个有趣的问题。
  • 真正的问题应该是,如果它使您的源代码更好看,是否值得额外处理字符串?
  • 编译时修复将是最佳的,但这会要求很多;)
  • 我被 Python 的 textwrap.dedent() 函数宠坏了,在我搜索 C# 等效项时发现了这个问题。

标签: c# .net string multiline verbatim-string


【解决方案1】:

字符串扩展怎么样?更新:我重读了您的问题,希望有更好的答案。这也是让我烦恼的事情,必须按如下方式解决它令人沮丧,但从好的方面来说它确实有效。

using System.Text.RegularExpressions;

namespace ConsoleApplication1
{
    public static class StringExtensions
    {
        public static string StripLeadingWhitespace(this string s)
        {
            Regex r = new Regex(@"^\s+", RegexOptions.Multiline);
            return r.Replace(s, string.Empty);
        }
    }
}

还有一个示例控制台程序:

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string x = @"This is a test
                of the emergency
                broadcasting system.";

            Console.WriteLine(x);

            Console.WriteLine();
            Console.WriteLine("---");
            Console.WriteLine();

            Console.WriteLine(x.StripLeadingWhitespace());

            Console.ReadKey();
        }
    }
}

还有输出:

This is a test
                of the emergency
                broadcasting system.

---

This is a test
of the emergency
broadcasting system.

如果您决定走这条路,还有一种更清洁的使用方式:

string x = @"This is a test
    of the emergency
    broadcasting system.".StripLeadingWhitespace();
// consider renaming extension to say TrimIndent() or similar if used this way

【讨论】:

  • 是的,这是我正在考虑的解决方案。可能需要添加智能以仅去除一定数量的空格或制表符,因为您可能需要在某些行上使用空格/制表符;)
  • 也许检查字符串是否以任何空格开头,如果是这样,默认将^\s+替换为该数量的空格?
【解决方案2】:

我想不出一个可以完全满足您的问题的答案,但是您可以编写一个函数,从字符串中包含的文本行中去除前导空格,并在每次创建此类字符串时调用它。

var myString = TrimLeadingSpacesOfLines(@" this is a 
    a multi-line string
    in c#.");

是的,这是一个 hack,但您在问题中指定了您接受 hack。

【讨论】:

  • 要注意的是-在某些情况下我可能需要一些前导空格,所以我只希望删除足够的空格,以便文本会碰到左边距...有意义吗?
【解决方案3】:

Cymen 提供了正确的解决方案。我使用与 Scala 的 stripMargin() 方法类似的方法。这是我的扩展方法的样子:

public static string StripMargin(this string s)
{
    return Regex.Replace(s, @"[ \t]+\|", string.Empty);
}

用法:

var mystring = @"
        |SELECT 
        |    *
        |FROM
        |    SomeTable
        |WHERE
        |    SomeColumn IS NOT NULL"
    .StripMargin();

结果:

SELECT 
    *
FROM
    SomeTable
WHERE
    SomeColumn IS NOT NULL

【讨论】:

    【解决方案4】:

    这是一个较长的解决方案,它试图尽可能地模仿textwrap.dedent。第一行保持原样,预计不会缩进。 (您可以使用doctest-csharp 生成基于文档测试的单元测试。)

    /// <summary>
    /// Imitates the Python's
    /// <a href="https://docs.python.org/3/library/textwrap.html#textwrap.dedent">
    /// <c>textwrap.dedent</c></a>.
    /// </summary>
    /// <param name="text">Text to be dedented</param>
    /// <returns>array of dedented lines</returns>
    /// <code doctest="true">
    /// Assert.That(Dedent(""), Is.EquivalentTo(new[] {""}));
    /// Assert.That(Dedent("test me"), Is.EquivalentTo(new[] {"test me"}));
    /// Assert.That(Dedent("test\nme"), Is.EquivalentTo(new[] {"test", "me"}));
    /// Assert.That(Dedent("test\n  me"), Is.EquivalentTo(new[] {"test", "  me"}));
    /// Assert.That(Dedent("test\n  me\n    again"), Is.EquivalentTo(new[] {"test", "me", "  again"}));
    /// Assert.That(Dedent("  test\n  me\n    again"), Is.EquivalentTo(new[] {"  test", "me", "  again"}));
    /// </code>
    private static string[] Dedent(string text)
    {
        var lines = text.Split(
            new[] {"\r\n", "\r", "\n"},
            StringSplitOptions.None);
    
        // Search for the first non-empty line starting from the second line.
        // The first line is not expected to be indented.
        var firstNonemptyLine = -1;
        for (var i = 1; i < lines.Length; i++)
        {
            if (lines[i].Length == 0) continue;
    
            firstNonemptyLine = i;
            break;
        }
    
        if (firstNonemptyLine < 0) return lines;
    
        // Search for the second non-empty line.
        // If there is no second non-empty line, we can return immediately as we
        // can not pin the indent.
        var secondNonemptyLine = -1;
        for (var i = firstNonemptyLine + 1; i < lines.Length; i++)
        {
            if (lines[i].Length == 0) continue;
    
            secondNonemptyLine = i;
            break;
        }
    
        if (secondNonemptyLine < 0) return lines;
    
        // Match the common prefix with at least two non-empty lines
        
        var firstNonemptyLineLength = lines[firstNonemptyLine].Length;
        var prefixLength = 0;
        
        for (int column = 0; column < firstNonemptyLineLength; column++)
        {
            char c = lines[firstNonemptyLine][column];
            if (c != ' ' && c != '\t') break;
            
            bool matched = true;
            for (int lineIdx = firstNonemptyLine + 1; lineIdx < lines.Length; 
                    lineIdx++)
            {
                if (lines[lineIdx].Length == 0) continue;
                
                if (lines[lineIdx].Length < column + 1)
                {
                    matched = false;
                    break;
                }
    
                if (lines[lineIdx][column] != c)
                {
                    matched = false;
                    break;
                }
            }
    
            if (!matched) break;
            
            prefixLength++;
        }
    
        if (prefixLength == 0) return lines;
        
        for (var i = 1; i < lines.Length; i++)
        {
            if (lines[i].Length > 0) lines[i] = lines[i].Substring(prefixLength);
        }
    
        return lines;
    }
    

    【讨论】:

    • 谢谢。我想要 textrwap.dedent 行为,它只去除第一行缩进的空格数量,而不是盲目地在每行开头的所有空格。而您的实现正是这样做的!
    • 能否请您仔细检查并举几个例子?它应该在第二行和下一行中寻找边距。今天我会尝试添加文档测试。
    • @kjellander 我添加了文档测试。如果您的具体输入未按预期产生结果,请告诉我。
    猜你喜欢
    • 2021-12-13
    • 1970-01-01
    • 2017-04-29
    • 1970-01-01
    • 2016-03-24
    • 1970-01-01
    • 1970-01-01
    • 2011-05-04
    • 2016-06-15
    相关资源
    最近更新 更多