C#|.net core 基礎 - 刪除字串最後一個字元的七大類N種實現方式

IT规划师發表於2024-10-10

今天想透過和大家分享如何刪除字串最後一個字元的N種實現方法,來回顧一些基礎知識點。

01、第一類、字串方式

這類方法是透過string型別自身方法直接實現。

1、Substring方法

相信大多數人第一個想到的可能就是這個方法。Substring方法是字串內建方法,可以透過指定起始索引位置為0以及長度為字串長度減1,直接擷取指定長度的子字串,從而達到刪除最後一個字元目的。

示例程式碼如下:

public static string StringSubstring(string source)
{
    return source.Substring(0, source.Length - 1);
}

2、範圍運算子

這個方法可以說是最簡潔的方法,可能大家用的不是很多。範圍運算子是從C# 8開始支援的。它的形式如:variate[start..end],指定某一索引範圍的開頭和末尾作為其運算元。左側運算元是範圍的包含性開頭。右側運算元是範圍的不包含性末尾。任一運算元都可以是序列開頭或末尾的索引。

下面列舉了表達集合範圍的各種方法:

範圍運算子也適用於字串,實現程式碼如下:

public static string StringRangeOperator(string source)
{
    return source[..^1];
}

3、Remove方法

Remove方法是字串內建方法,可以刪除從指定起始索引位置起到結尾的所有字元,因此可以把起始索引定為最後一個字元,從而達到刪除最後一個字元目的。

示例程式碼如下:

public static string StringRemove(string source)
{
    return source.Remove(source.Length - 1);
}

4、Create方法

Create方法是字串的靜態方法,這個方法相信大家用的比較少,其作用是建立一個具有特定長度的新字串,並在建立後使用指定的回撥對其進行初始化。下面我們直接看下實現程式碼:

public static string StringCreate(string source)
{
    return string.Create(source.Length - 1, source, (span, state) =>
    {
        for (var i = 0; i < state.Length - 1; i++)
        {
            span[i] = state[i];
        }
    });
}

下面對上面程式碼做個簡單解釋,第一個引數source.Length - 1是建立比原字串長度少1位的目標字串;第二個引數source是把原字串當作引數傳入,用於給第三個引數使用;第三個引數是一個兩個引數無返回值委託,其中span參數列示目標字串對應的Span,state參數列示原字串即第二個引數值,for迴圈即是把原字串字元迴圈賦值給目標字串。

5、小結

上面四種方法主要是使用了字串自身的內建方法進行操作,下面我們對四個方法進行三組對比效能測試,每組分別為長度為100、1000、10000的字串。

透過測試結果不難發現,除了Create方法,其他三個方法差別不大,綜合來看可以說Remove最優。

02、第二類、StringBuilder方式

如果需要對大量字串操作,相信大家會立即想到用StringBuilder來進行效能最佳化,下面簡單介紹兩種使用StringBuilder方式來刪除字串最後一個字元。

1、Append方法

字串就相當於字元陣列,因此我們可以迴圈字串,然後使用StringBuilder的Append方法進行拼接,實現程式碼如下:

public static string StringBuilderAppend(string source)
{
    var sb = new StringBuilder();
    for (var i = 0; i < source.Length - 1; i++)
    {
        sb.Append(source[i]);
    }
    return sb.ToString();
}

2、Length方式

相信大家看到這個標題應該比較疑惑,這是什麼意思,我們先看程式碼再講解:

public static string StringBuilderLength(string source)
{
    var sb = new StringBuilder(source);
    sb.Length--;
    return sb.ToString();
}

首先第一行程式碼表示透過原字串建立一個可變字串;重點就在第二行,直接對StringBuilder長度執行減1操作;最後再把StringBuilder轉為字串返回。

首先StringBuilder的Length屬性表示當前可變字串包含的字元數,當對其進行減1操作時,相當於告訴StringBuilder物件忽略最後一個字元,其內部並沒有真的刪除任何字元,被忽略的字元仍包含再StringBuilder物件內部,只是不再將其視為字串的一部分,因此在呼叫.ToString方法時返回的就是我們想要的字串。

3、小結

下面我們對兩個方法進行三組對比效能測試,每組分別為長度為100、1000、10000的字串。

透過這組測試結果很容易發現,直接操作Length屬性效能顯著優越於Append方法,但是和字串直接操作的方式相比還差了不少。

03、第三類、Array方式

上面我們提到字串相當於字元陣列,因此我們可以直接使用陣列相應的方法。

1、For方法

我們可以直接構建一個目標字元陣列,然後把原字串中相應的字元複製到新字元陣列中,最後把新字元陣列轉成字串返回即可,程式碼如下:

public static string ArrayFor(string source)
{
    var chars = new char[source.Length - 1];
    for (var i = 0; i < chars.Length; i++)
    {
        chars[i] = source[i];
    }
    return new string(chars);
}

2、Resize 方法

這個方法大家可能用的比較少,它可以把陣列元素個數更改為指定的大小。其思想有點像上面StringBuilder物件直接修改Length屬性。下面直接看看程式碼:

public static string ArrayResize(string source)
{
    var chars = source.ToCharArray();
    Array.Resize(ref chars, chars.Length - 1);
    return new string(chars);
}

3、CopyTo方法

這個方法相信大家應該有點影響,我們前面的文章也有提到過。簡單來說就是把原陣列複製到目標陣列中,程式碼如下:

public static string ArrayCopyTo(string source)
{
    var chars = new char[source.Length - 1];
    source.CopyTo(0, chars, 0, chars.Length);
    return new string(chars);
}

4、String方式

String方式是值當把原字串轉換為字元陣列後,直接使用String構造方法從字元陣列中指定位置處開始並指定長度,來獲取我們想要的結果。程式碼如下:

public static string ArrayString(string source)
{
    var chars = source.ToCharArray();
    return new string(chars, 0, chars.Length - 1);
}

其中字串建構函式第一個參數列示字元陣列,第二個參數列示從字元陣列第0個索引開始,第三個參數列示取字元陣列的元素個數。

5、小結

同樣對上面四種方法進行三組對比效能測試,每組分別為長度為100、1000、10000的字串。

透過測試結果不難發現,CopyTo方法和String方式相對較好,比之StringBuilder方式還要好些。

04、第四類、Linq方式

Linq方式的核心思想是透過Linq方法獲取目標字串對應的字元陣列,然後再轉為字串返回。

1、Take方法

Take方法主要作用是從序列的開頭返回指定數目的連續元素,因此程式碼實現如下:

public static string LinqTake(string source)
{
    return new string(source.Take(source.Length - 1).ToArray());
}

2、SkipLast方法

SkipLast方法是從C# 8才開始有的,其作用是返回集合排除最後指定個數的元素外的所有元素。

public static string LinqSkipLast(string source)
{
    return new string(source.SkipLast(1).ToArray());
}

3、Range + Select方法

Range方法相信大家用的也比較少,其作用是生成指定範圍內的整數序列。我們先來看程式碼然後再做解釋:

public static string LinqRange(string source)
{
    return new string(Enumerable.Range(0, source.Length - 1).Select(i => source[i]).ToArray());
}

這裡Range方法相當於生成了目標字串索引序列,即[0.. source.Length - 1],然後再透過Seletc方法取原字串相應的字元,最後得到結果。

4、小結

同樣對上面三種方法進行三組對比效能測試,每組分別為長度為100、1000、10000的字串。

透過測試結果不難發現,Range + Select方法相對較好,但是比之前幾類方法就差的太遠了。

05、第五類、Linq + String組合方式

這類方法是透過Linq方法和字串方法組合的方式實現。

1、Concat方法

Concat方法是字串的靜態方法可以連線多個字元成為一個新的字串,然後透過Linq的SkipLast方法配合達到我們的目的,程式碼如下:

public static string LinqStringConcat(string source)
{
    return string.Concat(source.SkipLast(1));
}

2、Join方法

Join方法也是字串的靜態方法,主要作用是使用指定的分隔符連線集合的成員。因此也可以達到Concat類似的效果。

public static string LinqStringJoin(string source)
{
    return string.Join("", source.SkipLast(1));
}

3、小結

下面我們對兩個方法進行三組對比效能測試,每組分別為長度為100、1000、10000的字串。

透過這組測試結果說明兩者相差不大,相對於之前的方法更差了。

06、第六類、資料檢視方式

資料檢視方式的核心思想是透過Span、Memory和ArraySegment實現。

1、AsSpan方法

Span是一個輕量級的、非託管的檢視,用於表示連續的記憶體塊。它可以直接操作棧上的記憶體。AsSpan方法可以透過指定起始索引和長度,直接在原字串上獲取到目標字串檢視,然後轉成字串返回,程式碼實現如下:

public static string Span(string source)
{
    var span = source.AsSpan(0, source.Length - 1);
    return new string(span);
}

2、AsMemory方法

Memory也是一個記憶體檢視,但與 Span 不同,它可以儲存在 heap 上。AsMemory方法用法和AsSpan方法類似,程式碼如下:

public static string Memory(string source)
{
    var memory = source.AsMemory(0, source.Length - 1);
    return new string(memory.Span);
}

3、ArraySegment方法

ArraySegment封裝了對陣列的一部分的引用,並維護了該部分的起始位置和長度。

public static string ArraySegment(string source)
{
    var segment = new ArraySegment<char>(source.ToCharArray(), 0, source.Length - 1);
    return new string(segment.Array, segment.Offset, segment.Count);
}

4、小結

同樣對上面三種方法進行三組對比效能測試,每組分別為長度為100、1000、10000的字串。

透過測試結果可以發現,三種方法效能都是相當高,當然其中ArraySegment方法相對要差一些。總統來說資料檢視方式已經和第一類字串方式不相上下了。

07、第七類、正規表示式方式

這裡解釋兩種正規表示式實現的方法。

1、Replace方法

Replace方法是Regex的靜態方法,程式碼如下:

public static string RegexReplace(string source)
{
    return Regex.Replace(source, ".$", "");
}

2、Match方法

Match方法也是Regex的靜態方法,程式碼如下:

public static string RegexMatch(string source)
{
    var match = Regex.Match(source, @"^(.*).$");
    return match.Groups[1].Value;
}

3、小結

下面我們對兩個方法進行三組對比效能測試,每組分別為長度為100、1000、10000的字串。

透過這組測試結果說明兩者相差不大,相對於之前的方法效能差別居中。

從整體來看,使用第一類字串方式效能又高程式碼又簡潔是最優選,而列舉了那麼多種方法主要目的還是熟悉一些基礎方法,雖然在這個案例裡不是最優解,但是說不定在其他地方就用的恰到好處。

我們都知道做同樣一件事件可能有很多種方法,然後可以選擇出一種最優的方法,但是這個前提是你要知道這些方法是什麼,你才能有的選。

:測試方法程式碼以及示例原始碼都已經上傳至程式碼庫,有興趣的可以看看。https://gitee.com/hugogoos/Planner

相關文章