23

文字列の一部のみを取得する場合は、部分文字列メソッドが主に使用されます。これには、エラーを回避するために最初に文字列の長さをテストする必要があるという欠点があります。たとえば、データをデータベースに保存し、値を最初の 20 文字に切り詰めたいとします。

temp.substring(0,20) を実行しても temp が 10 文字しか保持しない場合、例外がスローされます。

私が見る2つの解決策があります:

  1. 長さをテストし、必要に応じて部分文字列を実行します
  2. 拡張メソッドTakeを使用する

        string temp = "1234567890";
        var data= new string( temp.Take(20).ToArray());
        --> data now holds "1234657890"
    

Takeメソッドを使用する場合、速度またはメモリ使用の点で不利な点はありますか? 利点は、if ステートメントをすべて記述する必要がないことです。

4

6 に答える 6

24

これを頻繁に行う場合は、拡張メソッドを作成してみませんか?

例えば:

using System;

namespace Demo
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("123456789".Left(5));
            Console.WriteLine("123456789".Left(15));
        }
    }

    public static class StringExt
    {
        public static string Left(this string @this, int count)
        {
            if (@this.Length <= count)
            {
                return @this;
            }
            else
            {
                return @this.Substring(0, count);
            }
        }
    }
}
于 2013-03-14T10:05:57.203 に答える
14

Henk Holtermand が言ったように、Take()を作成し、次にコールIEnumeratorが必要です。ToArray()

そのため、アプリケーションでパフォーマンスが重要な場合、またはプロセスで部分文字列を数回実行する場合、パフォーマンスが問題になる可能性があります。

メソッドがどれだけ遅いかを正確にベンチマークするサンプルプログラムを作成しましTake()た。結果は次のとおりです。

1,000 万回テスト済み:

  • 部分文字列の実行時間: 266 ミリ秒
  • テイク操作の実行時間: 1437 ms

コードは次のとおりです。

    internal const int RETRIES = 10000000;

    static void Main(string[] args)
    {
        string testString = Guid.NewGuid().ToString();

        long timeSubstring = MeasureSubstring(testString);
        long timeTake = MeasureTake(testString);

        Console.WriteLine("Time substring: {0} ms, Time take: {1} ms",
            timeSubstring, timeTake);
    }

    private static long MeasureSubstring(string test)
    {
        long ini = Environment.TickCount;

        for (int i = 0; i < RETRIES; i++)
        {
            if (test.Length > 4)
            {
                string tmp = test.Substring(4);
            }
        }

        return Environment.TickCount - ini;
    }

    private static long MeasureTake(string test)
    {
        long ini = Environment.TickCount;

        for (int i = 0; i < RETRIES; i++)
        {
            var data = new string(test.Take(4).ToArray());
        }

        return Environment.TickCount - ini;
    }
于 2013-03-14T10:08:02.173 に答える
6

最初に私は答えたくありませんでした(すでに有効な答えがあるので)、しかし私はコメントとして合わない何かを追加したいと思います:

あなたはパフォーマンス/メモリの問題について話している。右。他の人が言ったstring.SubStringように、それが内部的に最適化される方法とLINQがどのように機能するかstring.Take()(文字の列挙など)のために、はるかに効率的です。

誰も言わなかったことはTake()、あなたの場合の主な欠点は、部分文字列の単純さを完全に破壊することです。ティムが言ったように、あなたが望む実際の文字列を取得するには、次のように書く必要があります。

string myString = new string(temp.Take(20).ToArray());

くそー...これは(マシューの拡張メソッドを参照)よりも理解するのが非常に難しいです:

string myString = temp.Left(20);

LINQは多くのユースケースに最適ですが、必要がない場合は使用しないでください。単純なループでさえ、LINQよりも優れている場合があります(つまり、より高速で、読みやすく、理解しやすい)ので、単純な部分文字列を想像してみてください...

あなたの場合のLINQについて要約すると:

  • パフォーマンスの低下
  • 読みにくい
  • 理解しにくい
  • LINQが必要です(たとえば、.Net 2.0では機能しません)
于 2013-03-14T10:44:49.767 に答える
2

Takeメソッドを使用する場合、速度またはメモリ使用の点で不利なことはありますか

はい。Take()最初の文字を作成し、各文字について、およびなどIEnumerator<char>のフープを通過する必要があります。また、ToArray と文字列コンストラクターにも注意してください。MoveNext()yield return;

文字列の数が少ない場合は問題ありませんが、大きなループでは、特殊化された文字列関数の方がはるかに優れています。

于 2013-03-14T10:03:03.307 に答える
1

拡張メソッドは部分文字列を作成しません。(ToArray) または(ToList)Takeを作成するために使用できるクエリを返します。しかし、実際にはその部分文字列が必要です。Char[]List<Char>

次に、他の方法も必要です。

string  data = new string(temp.Take(20).ToArray());

これは暗黙的に a を使用しforeachて char を列挙し、新しい char[] を作成します (2 倍アルゴリズムのために割り当てすぎるサイズになる可能性があります)。最後に、新しい文字列が から作成されますchar[]

一方、最適化された方法Substringを使用します。

したがって、このわずかな便利さは、無視できるかもしれませんが、常にそうとは限らないメモリで支払われます。

于 2013-03-14T10:05:21.523 に答える