我喜欢在c#中使用@"strings",特别是当我有很多多行文本时。唯一令人烦恼的是,当我这样做时,我的代码格式会变得一团糟,因为第二行和更大的行被完全推到左边,而不是使用格式优美的代码的缩进。我知道这是设计的,但是是否有一些选项/hack方法允许这些行被缩进,而不添加实际的制表符/空格到输出?
添加示例:
var MyString = @" this is
a multi-line string
in c#.";
我的变量声明被缩进到"正确"的深度,但是字符串中的第二行和后面的行被推到左边——所以代码有点难看。您可以在第2行和第3行开始添加制表符,但是字符串本身将包含这些制表符…有意义吗?
如何扩展字符串?更新:我重读了你的问题,希望有更好的答案。这也是困扰我的事情,不得不像下面这样解决它是令人沮丧的,但从好的方面来看,它确实有效。
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
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
在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}.
""";
我想不出一个答案可以完全满足你的问题,但是你可以编写一个函数,从字符串中包含的文本行中去掉前导空格,并在每次创建这样的字符串时调用它。
var myString = TrimLeadingSpacesOfLines(@" this is a
a multi-line string
in c#.");
是的,这是一个hack,但是你在你的问题中明确了你接受一个hack。
这是一个较长的解决方案,试图模仿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("testnme"), Is.EquivalentTo(new[] {"test", "me"}));
/// Assert.That(Dedent("testn me"), Is.EquivalentTo(new[] {"test", " me"}));
/// Assert.That(Dedent("testn men again"), Is.EquivalentTo(new[] {"test", "me", " again"}));
/// Assert.That(Dedent(" testn men again"), Is.EquivalentTo(new[] {" test", "me", " again"}));
/// </code>
private static string[] Dedent(string text)
{
var lines = text.Split(
new[] {"rn", "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;
}