C#中使用字符串参数的TrimStart函数

96
我正在寻找接受字符串参数的 TrimStart() TrimEnd() 的字符串扩展方法。 我可以自己编写,但我总是对看到其他人如何做事情感兴趣。 如何实现这一点?

4
您的问题不是很明确。在trim函数中,字符串参数应该具体执行什么操作?(假设您指的不是扩展方法的强制“这个字符串”语法) - asawyer
2
所以你想要两个与TrimStart/TrimEnd相同功能的函数?还是你想要一个函数来从开头/结尾删除输入字符? - Blam
1
是的,与TrimStart和TrimEnd具有相同的功能,但接受字符串而不是字符。 - Mike Cole
在 .Net 中,字符串具有 Trim、TrimEnd 和 TrimStart 方法,您不想使用它们吗?还是您想看看如何通过学习来完成这个任务? - jwwishart
9个回答

140

要修剪所有出现的(完全匹配的)字符串,可以使用类似以下代码:

TrimStart

public static string TrimStart(this string target, string trimString)
{
    if (string.IsNullOrEmpty(trimString)) return target;

    string result = target;
    while (result.StartsWith(trimString))
    {
        result = result.Substring(trimString.Length);
    }

    return result;
}

TrimEnd

public static string TrimEnd(this string target, string trimString)
{
    if (string.IsNullOrEmpty(trimString)) return target;

    string result = target;
    while (result.EndsWith(trimString))
    {
        result = result.Substring(0, result.Length - trimString.Length);
    }

    return result;
}

要从目标字符串的开头或结尾修剪trimChars参数中的任何字符(例如"foobar'@"@';".TrimEnd(";@'")将返回"foobar"),您可以使用以下方法:

TrimStart

public static string TrimStart(this string target, string trimChars)
{
    return target.TrimStart(trimChars.ToCharArray());
}

TrimEnd

public static string TrimEnd(this string target, string trimChars)
{
    return target.TrimEnd(trimChars.ToCharArray());
}

17
这不是一个好的解决方案,因为它会截断部分匹配的字符串:"foobartes"。调用TrimEnd("test".ToCharArray())的结果是foobar。依我之见,只有在精确匹配时才应该进行截断。 编辑:快速修复方法是在截断前添加一个target.EndsWith(trimChars)检查。 - Contra
同意,这听起来不像是op所请求的。我认为他们想要从一个较大的字符串开头修剪某个特定的字符串。简单地修剪字符串中的所有字符可能会删除一些不完全匹配的情况。 - Tim
2
感谢您提供 string.ToCharArray() 的提示。 - Hamid Sadeghian
7
说实话,关于TrimStart/End(trimChars.ToCharArray())的部分应该删除。除了在简单测试中以外,在生产环境中这个方法基本上很难得到期望的效果,并且可能会破坏字符串。 - Chris Marisic
1
如果 trimString == "",这将永远循环,而且不确定您是否总是想要 "12123".TimeStart("12") == "3" - smg
显示剩余2条评论

17

TrimStart和TrimEnd都接受一个字符数组作为参数。这意味着你可以像这样将字符串传递给字符数组:

var trimChars = " .+-";
var trimmed = myString.TrimStart(trimChars.ToCharArray());

所以我认为没有必要提供接受一个字符串参数的重载。


1
哇,这真是尴尬。我没意识到它是这样工作的。谢谢! - Mike Cole
43
请注意,此解决方案按任意顺序匹配修剪字符。例如,如果 myString="Sammy"trimChars="Sam",它将返回 "y",但预期结果是 "my"。因此,它修剪得太多,并未将修剪字符串视为整体。 - Matt
2
正如 @Matt 所解释的那样,这种方法有一定的风险。这是因为 Trim(Start/End) 接受一个字符数组作为参数,而不是一个字符串。在给出的例子中,你也可以有 myString="Sammy"trimChars="SaX";这将导致 "mmy"。请确保这是你想要的行为。 - Andrew

12

我认为这个问题是想要从一个较长的字符串的开头修剪掉指定的子字符串。

例如,如果我的字符串是"hellohellogoodbyehello",如果你试图调用TrimStart("hello"),你将得到"goodbyehello"。

如果是这种情况,你可以使用以下代码:

string TrimStart(string source, string toTrim)
{
    string s = source;
    while (s.StartsWith(toTrim))
    {
        s = s.Substring(toTrim.Length - 1);
    }
    return s;
}

如果你只需要对几个字符串进行处理,那么这种方式是简单而且能够完成任务的,但是如果你需要经常进行字符串处理的话,这种方式就不是很高效。


7
这应该是 toTrim.length,没有 -1,对吗? - Evan Trimboli

6
为了匹配整个字符串并且不分配多个子字符串,您应该使用以下内容:
    public static string TrimStart(this string source, string value, StringComparison comparisonType)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        int valueLength = value.Length;
        int startIndex = 0;
        while (source.IndexOf(value, startIndex, comparisonType) == startIndex)
        {
            startIndex += valueLength;
        }

        return source.Substring(startIndex);
    }

    public static string TrimEnd(this string source, string value, StringComparison comparisonType)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        int sourceLength = source.Length;
        int valueLength = value.Length;
        int count = sourceLength;
        while (source.LastIndexOf(value, count, comparisonType) == count - valueLength)
        {
            count -= valueLength;
        }

        return source.Substring(0, count);
    }

谢谢,代码写得很好。我在我的应用程序中对它进行了一些微小的更改,就是在这种情况下使用 if (source == null){return null;} 。我认为这是一个更好的选择。 - Ramin Bateni

3

来源自dotnetperls.com

性能

遗憾的是,TrimStart方法并未进行过大量优化。在特定情况下,您很可能能够编写基于字符迭代的代码,从而比其更快地运行。这是因为使用TrimStart需要创建一个数组。

然而:自定义代码不一定需要一个数组。但对于开发速度较快的应用程序来说,使用TrimStart方法是很有用的。


1
那么如何实现呢?我想这就是问题所在。 - Matt

3

在C#中没有内置函数 - 但是您可以编写自己的扩展方法,我将在下面向您展示 - 它们可以像内置函数一样用于修剪字符,但是您可以使用字符串进行修剪。

请注意,使用IndexOf / LastIndexOf,您可以选择是否区分大小写/区域设置敏感。

我还实现了"重复修剪"功能(请参见可选参数)。

用法:

// myStr:   the string that needs to be trimmed
// trimStr: the string to trim from myStr
var trimmed1 = myStr.TrimStart(trimStr); 
var trimmed2 = myStr.TrimEnd(trimStr);
var trimmed3 = myStr.TrimStr(trimStr);
var trimmed4 = myStr.Trim(trimStr);

有一个函数TrimStr(..)可以从字符串的开头和结尾进行修剪,还有三个函数实现.TrimStart(...).TrimEnd(...).Trim(..)以与.NET修剪兼容:

在DotNetFiddle中试一试

public static class Extension
{
    public static string TrimStr(this string str, string trimStr, 
                  bool trimEnd = true, bool repeatTrim = true,
                  StringComparison comparisonType = StringComparison.OrdinalIgnoreCase)
    {
        int strLen;
        do
        {
            strLen = str.Length;
            {
             if (trimEnd)
                {
                  if (!(str ?? "").EndsWith(trimStr)) return str;
                  var pos = str.LastIndexOf(trimStr, comparisonType);
                  if ((!(pos >= 0)) || (!(str.Length - trimStr.Length == pos))) break;
                  str = str.Substring(0, pos);
                }
                else
                {
                  if (!(str ?? "").StartsWith(trimStr)) return str;
                  var pos = str.IndexOf(trimStr, comparisonType);
                  if (!(pos == 0)) break;
                  str = str.Substring(trimStr.Length, str.Length - trimStr.Length);
                }
            }
        } while (repeatTrim && strLen > str.Length);
        return str;
    }

     // the following is C#6 syntax, if you're not using C#6 yet
     // replace "=> ..." by { return ... }

    public static string TrimEnd(this string str, string trimStr, 
            bool repeatTrim = true,
            StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) 
            => TrimStr(str, trimStr, true, repeatTrim, comparisonType);

    public static string TrimStart(this string str, string trimStr, 
            bool repeatTrim = true,
            StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) 
            => TrimStr(str, trimStr, false, repeatTrim, comparisonType);

    public static string Trim(this string str, string trimStr, bool repeatTrim = true,
        StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) 
        => str.TrimStart(trimStr, repeatTrim, comparisonType)
              .TrimEnd(trimStr, repeatTrim, comparisonType);

}

现在你可以像这样使用它
    Console.WriteLine("Sammy".TrimEnd("my"));
    Console.WriteLine("Sammy".TrimStart("Sam"));
    Console.WriteLine("moinmoin gibts gips? gips gibts moin".TrimStart("moin", false));
    Console.WriteLine("moinmoin gibts gips? gips gibts moin".Trim("moin").Trim());

这段代码创建输出结果。

Sam
我的
早上有石膏吗?有石膏,早上有
有石膏吗?有石膏

在最后一个例子中,trim函数会进行重复修剪,将字符串开头和结尾的所有“我的”都修剪掉。而前面的例子只会从开头修剪一次“我的”。


请注意,TrimStr在Trim、TrimStart和TrimEnd内部使用。 - Matt
区别在于您可以使用TrimStr来通过参数决定您是要从字符串的开头还是结尾进行修剪。 - Matt

-1

我猜你的意思是,例如给定字符串“HelloWorld”,并调用函数以“Hello”开头的“trim”函数,你将得到“World”。我认为这实际上是一个子字符串操作,因为你正在删除已知长度的字符串部分,而不是删除未知长度的字符串的修剪操作。

因此,我们创建了几个扩展方法,命名为SubstringAfterSubstringBefore。它们可以很好地放在框架中,但事实并非如此,所以您需要自己实现它们。不要忘记使用Ordinal作为默认值,同时具有StringComparison参数。


-1
如果你不想使用内置的修剪函数,出于任何原因,假设你想使用一个输入字符串来修剪,比如 " ~!" ,以实现与内置的TrimStart和 [' ', '~', '!'] 本质相同的效果。
public static String TrimStart(this string inp, string chars)
{
    while(chars.Contains(inp[0]))
    {
        inp = inp.Substring(1);
    }

    return inp;
}

public static String TrimEnd(this string inp, string chars)
{
    while (chars.Contains(inp[inp.Length-1]))
    {
        inp = inp.Substring(0, inp.Length-1);
    }

    return inp;
}

-2

用一个字符串参数来修剪字符串开头/结尾的函数,但只能执行一次(无循环,这种单一情况更常见,可以通过额外的参数添加循环触发):

public static class BasicStringExtensions
{
    public static string TrimStartString(this string str, string trimValue)
    {
        if (str.StartsWith(trimValue))
            return str.TrimStart(trimValue.ToCharArray());
        //otherwise don't modify
        return str;
    }
    public static string TrimEndString(this string str, string trimValue)
    {
        if (str.EndsWith(trimValue))
            return str.TrimEnd(trimValue.ToCharArray());
        //otherwise don't modify
        return str;
    }
}

如之前所述,如果您想实现“while循环”方法,请务必检查空字符串,否则它会无限循环。


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