从 .NET 中的字符串末尾修剪字符串 - 为啥会丢失?

Posted

技术标签:

【中文标题】从 .NET 中的字符串末尾修剪字符串 - 为啥会丢失?【英文标题】:Trim string from the end of a string in .NET - why is this missing?从 .NET 中的字符串末尾修剪字符串 - 为什么会丢失? 【发布时间】:2011-08-24 05:38:27 【问题描述】:

我一直都需要这个,并且一直对 Trim()、TrimStart() 和 TrimEnd() 函数不将字符串作为输入感到沮丧。你在一个字符串上调用 EndsWith() ,并找出它是否以另一个字符串结尾,但是如果你想从末尾删除它,你必须做子字符串黑客来做到这一点(或调用 Remove() 并祈祷它是唯一的实例...)

为什么.NET 中缺少这个基本功能?其次,任何关于实现这一点的简单方法的建议(最好不是正则表达式路线......)

【问题讨论】:

您想要返回"AB""ABCDCDCD".TrimEnd("CD") 还是返回"ABCDCD""ABCDCDCD".RemoveIfEndsWith("CD") 出于我的目的,我对第二个感兴趣 - 删除单个实例。所以我猜appprop名称是RemoveEnd()?可以删除字符串的所有实例,甚至可以修剪字符串集合的替代选项也会非常好......但与当前需要不同。我实际上可能会为此问另一个问题。 我猜它不存在的原因是因为如果添加了所有可能有用的方法,那么 c# 字符串将变得像 std:string - 充满了所有可以想象的额外内容,包括 str.DoesSomeofStringMAtchThisLintFromBehindTheSofa() 等。事实上,c# 有一种非常好的方法可以自己添加方法,这意味着这不是一个真正的问题。如果您反对,因为它内置而不是基于自制的子字符串更有效,那么我已经在考虑在我的代码中使用子字符串的频率时遇到了很大的麻烦。 我知道必须在某处画出界线,但天哪,这是基本的东西。这和 PickBellyButtonLint()。 我同意,这是智障。甚至 VB6 也可以做到这一点,但在当前版本中仍然没有添加。巨大的疏忽?技术原因? .. 还是只是为了惹恼我们? 【参考方案1】:

编辑 - 包含在一个方便的扩展方法中:

public static string TrimEnd(this string source, string value)

    if (!source.EndsWith(value))
        return source;

    return source.Remove(source.LastIndexOf(value));

所以你可以做s = s.TrimEnd("DEF");

【讨论】:

你的第一个实例(首先不检查“endswith”)我认为如果它不在它会清空整个字符串? ;) 单行版source.EndsWith(value) ? source.Remove(source.LastIndexOf(value, StringComparison.Ordinal)) : source 我将这个扩展方法命名为 TrimIfEndsWith 如果你传递空字符串,它仍然会在末尾删除一个字符【参考方案2】:

TrimEnd()(和其他修剪方法)接受要修剪的字符,但不接受字符串。如果您真的想要一个可以修剪整个字符串的版本,那么您可以创建一个扩展方法。比如……

public static string TrimEnd(this string input, string suffixToRemove, StringComparison comparisonType = StringComparison.CurrentCulture)

    if (suffixToRemove != null && input.EndsWith(suffixToRemove, comparisonType)) 
    
        return input.Substring(0, input.Length - suffixToRemove.Length);
    

    return input;

然后可以像内置方法一样调用它。

【讨论】:

我建议将代码包装在 while 中,因为这模拟了标准 Trim 函数的功能。请参阅我的答案以更新您的代码 Daniel 我认为您应该重命名该方法,因为接受 char 数组的 .NET 版本会删除所有这些字符。相反,您正在实现具有不同行为的重载。 添加了要传入的 StringComparison 参数。您也可以将其默认为 comparisonType = StringComparison.OrdinalIgnoreCase 或您想要的任何一个【参考方案3】:

使用 Daniel 的代码并在一段时间内将其包装起来,而不是直接使用 if 提供的功能更类似于 Microsoft Trim 函数:

public static string TrimEnd(this string input, string suffixToRemove)

    while (input != null && suffixToRemove != null && input.EndsWith(suffixToRemove))
    
        input = input.Substring(0, input.Length - suffixToRemove.Length);
    
    return input;

【讨论】:

这是与其他答案不同的行为...尝试例如在"exceeded".TrimEnd("ed") 之类的字符串上运行【参考方案4】:

我敲了这个快速扩展方法。

不肯定它有效(我现在无法测试它),但理论是合理的。

    public static string RemoveLast(this string source, string value)
    
        int index = source.LastIndexOf(value);
        return index != -1 ? source.Remove(index, value.Length) : source;
    

【讨论】:

它会删除最后一次出现,但不能确保最后一次出现也是字符串的结尾(请参阅关于EndsWith 的 OP 帖子)... Yahia:啊,没想到这是要求的一部分。 :) 阅读理解失败。【参考方案5】:

在这种情况下,正则表达式替换可能是你的朋友。

var str = "Hello World!";
str = Regex.Replace(str, @"World!$", "");
//str == "Hello"

【讨论】:

Regex 通常比普通字符串方法更占用资源。【参考方案6】:

这是你反对必须做的事?

if (theString.endsWith(theOtherString))

   theString = theString.Substring(0, theString.Length - theOtherString.Length);

【讨论】:

我认为这就是 OP 所说的“子字符串破解” 我同意 boomhauer 的观点,即这个功能可以在 String 类中作为重载提供。但是,这没什么大不了的,可以在实用程序类中处理,以免因必须输入而“经常感到沮丧”。 OP 想要一个子字符串,但不想使用 Substring 方法 - 而 我是 反对者?好吧,随便... :-) 我觉得最让我沮丧的是,我们已经有了 Trim() 函数,为什么他们不允许它也接受字符串。非常简单的选项,非常有用...【参考方案7】:

我喜欢我的 TrimEnd 删除字符串末尾的所有实例,而不仅仅是最后一个。

public static string TrimEnd(this string str, string trimStr)

    if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(trimStr)) return str;

    while(str.EndsWith(trimStr))
    
        str = str.Remove(str.LastIndexOf(trimStr));
    
    return str;

【讨论】:

【参考方案8】:

Trim()、TrimStart() 和 TrimEnd() 是替换所有出现的 same 字符的方法。这意味着您只能删除一系列空白或一系列点。

您可以使用正则表达式替换来完成此操作:

string s1 = "This is a sentence.TRIMTHIS";
string s2 = System.Text.RegularExpressions.Regex.Replace(s1, @"TRIMTHIS$", "");

为方便起见,您可以将其包装在扩展方法中:

public static string TrimStringEnd(this string text, string removeThis)

    return System.Text.RegularExpressions.Regex.Replace(s1, removeThis, "");

这样称呼它

string s2 = (@"This is a sentence.TRIMTHIS").TrimStringEnd(@"TRIMTHIS");

【讨论】:

OP 确实提到他更喜欢不使用正则表达式的东西 还请记住,您必须转义 removeThis。如Regex.Replace(s1, Regex.Escape(removeThis), "")。否则你会得到正则表达式匹配行为而不是字符串匹配行为。【参考方案9】:

这是我想出的扩展方法(从该问题的现有答案中汲取的大量灵感)来补充现有的 TrimEnd 方法;它需要一个可选的布尔值,只允许删除字符串的一个尾随实例,而不是所有尾随实例。

/// <summary>
/// Removes trailing occurrence(s) of a given string from the current System.String object.
/// </summary>
/// <param name="trimSuffix">A string to remove from the end of the current System.String object.</param>
/// <param name="removeAll">If true, removes all trailing occurrences of the given suffix; otherwise, just removes the outermost one.</param>
/// <returns>The string that remains after removal of suffix occurrence(s) of the string in the trimSuffix parameter.</returns>
public static string TrimEnd(this string input, string trimSuffix, bool removeAll = true) 
    while (input != null && trimSuffix != null && input.EndsWith(trimSuffix)) 
        input = input.Substring(0, input.Length - trimSuffix.Length);

        if (!removeAll) 
            return input;
        
    

    return input;

【讨论】:

【参考方案10】:
.TrimStart("AB".ToCharArray())

【讨论】:

不应该是.TrimEnd("AB".ToCharArray()); 吗? 即使是这样,这将修剪的不仅仅是字符串“AB”【参考方案11】:

我最近需要一种高性能的方法来从字符串的开头/结尾删除单个或多个字符串实例。我想出的这个实现在字符串的长度上是 O(n),避免了昂贵的分配,并且根本不使用跨度调用 SubString

Substring hack!(好吧,现在我编辑了我的帖子)。

    public static string Trim(this string source, string whatToTrim, int count = -1) 
        => Trim(source, whatToTrim, true, true, count);

    public static string TrimStart(this string source, string whatToTrim, int count = -1) 
        => Trim(source, whatToTrim, true, false, count);

    public static string TrimEnd(this string source, string whatToTrim, int count = -1) 
        => Trim(source, whatToTrim, false, true, count);

    public static string Trim(this string source, string whatToTrim, bool trimStart, bool trimEnd, int numberOfOccurrences)
    
        // source.IsNotNull(nameof(source));  <-- guard method, define your own
        // whatToTrim.IsNotNull(nameof(whatToTrim));  <-- "

        if (numberOfOccurrences == 0 
            || (!trimStart && !trimEnd) 
            || whatToTrim.Length == 0 
            || source.Length < whatToTrim.Length)
            return source;

        int start = 0, end = source.Length - 1, trimlen = whatToTrim.Length;

        if (trimStart)
            for (int count = 0; start < source.Length; start += trimlen, count++)
            
                if (numberOfOccurrences > 0 && count == numberOfOccurrences)
                    break;
                for (int i = 0; i < trimlen; i++)
                    if ((source[start + i] != whatToTrim[i] && i != trimlen) || source.Length - start < trimlen)
                        goto DONESTART;
            

        DONESTART:
        if (trimEnd)
            for (int count = 0; end > -1; end -= trimlen, count++)
            
                if (numberOfOccurrences != -1 && count == numberOfOccurrences)
                    break;

                for (int i = trimlen - 1; i > -1; --i)
                    if ((source[end - trimlen + i + 1] != whatToTrim[i] && i != 0) || end - start + 1 < trimlen)
                        goto DONEEND;
            

        DONEEND:
        return source.AsSpan().Slice(start, end - start + 1).ToString();
    

【讨论】:

我删除了这些行。那是代码中的一个保护方法。【参考方案12】:

以下示例演示了如何通过将空格和标点符号视为分隔符来从文本块中提取单个单词。传递给String.Split(Char[])方法的separator参数的字符数组由一个空格字符和一个制表符以及一些常用的标点符号组成。

string words ="sfdgdfg-121";
string [] split = words.Split(new Char [] ' ', ',', '.', ':', '-' );
foreach (string s in split)

    if (s.Trim() != "")              
        Console.WriteLine(s);

试试这个代码。

【讨论】:

这甚至没有接近回答 OP 的问题。【参考方案13】:

至于为什么缺少你想要的功能,我怀疑是因为设计师没有看到它像你想象的那样普遍或基本。正如您从其他答案中看到的那样,复制是一件很容易的事情。

这是我用来做的方法。

public static string TrimEnd(string input, string suffixToRemove)

    if (input == null)
        throw new ArgumentException("input cannot be null.");
    if (suffixToRemove == null)
        throw new ArgumentException("suffixToRemove cannot be null.");
    int pos = input.LastIndexOf(suffixToRemove);
    if (pos == (input.Length - suffixToRemove.Length))
        return input.Substring(0, pos);
    return input;

【讨论】:

-1 抱歉,有很多例外。例外是针对……例外情况。尝试修剪为 null 或带有 null 后缀的字符串并不例外,实际上这是意料之中的。

以上是关于从 .NET 中的字符串末尾修剪字符串 - 为啥会丢失?的主要内容,如果未能解决你的问题,请参考以下文章

我无法使用修剪后的版本更新列中的值。为啥?

为啥我们需要在 C 中的字符数组末尾添加一个'\0'(null)?

如何从 JavaScript 中的字符串修剪文件扩展名?

为啥从sqlserver里读出的字符串末尾含空格?

去除修剪特殊字符

为啥从 XML 导入 Access 数据库时数据会被截断