C# 中逐字字符串的多行格式(前缀为 @)

发布于 2024-12-01 08:21:46 字数 367 浏览 0 评论 0原文

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

添加示例:

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

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

I love using the @"strings" in c#, especially when I have a lot of multi-line text. The only annoyance is that my code formatting goes to doodie when doing this, because the second and greater lines are pushed fully to the left instead of using the indentation of my beautifully formatted code. I know this is by design, but is there some option/hack way of allowing these lines to be indented, without adding the actual tabs/spaces to the output?

adding example:

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

My variable declaration is indented to the "correct" depth, but the second and further lines in the string get pushed to the left margin- so the code is kinda ugly. You could add tabs to the start of line 2 and 3, but the string itself would then contain those tabs... make sense?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

樱花落人离去 2024-12-08 08:21:46

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

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

How about a string extension? Update: I reread your question and I hope there is a better answer. This is something that bugs me too and having to solve it as below is frustrating but on the plus side it does work.

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

And an example console program:

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

And the output:

This is a test
                of the emergency
                broadcasting system.

---

This is a test
of the emergency
broadcasting system.

And a cleaner way to use it if you decide to go this route:

string x = @"This is a test
    of the emergency
    broadcasting system.".StripLeadingWhitespace();
// consider renaming extension to say TrimIndent() or similar if used this way
短叹 2024-12-08 08:21:46

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

Cymen has given the right solution. I use a similar approach as derived from Scala's stripMargin() method. Here's what my extension method looks like:

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

Usage:

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

Result:

SELECT 
    *
FROM
    SomeTable
WHERE
    SomeColumn IS NOT NULL
z祗昰~ 2024-12-08 08:21:46

对于 C# 11,您现在可以使用 原始字符串文字

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

输出是:

this is
a multi-line string
in c#.

它还与字符串插值结合:

var variable = 24.3;
var myString = $"""
    this is 
    a multi-line string
    in c# with a {variable}.
    """;

with C# 11 you can now use raw string literals.

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

The output is:

this is
a multi-line string
in c#.

It also combines with string interpolation:

var variable = 24.3;
var myString = 
quot;""
    this is 
    a multi-line string
    in c# with a {variable}.
    """;
夏夜暖风 2024-12-08 08:21:46

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

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

是的,这是一个黑客攻击,但您在问题中指定了接受黑客攻击。

I can't think of an answer that would completely satisfy your question, however you could write a function that strips leading spaces from lines of text contained in a string and call it on each creation of such a string.

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

Yes it is a hack, but you specified your acceptance of a hack in your question.

倾城月光淡如水﹏ 2024-12-08 08:21:46

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

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

Here is a longish solution which tries to mimic textwrap.dedent as much as possible. The first line is left as-is and expected not to be indented. (You can generate the unit tests based on the doctests using 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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文