.NET文字列を切り捨てる方法を教えてください。


406

文字列の長さが指定された値を超えないように文字列を切り捨てたいのですが。私はデータベーステーブルに書き込んでおり、書き込む値が列のデータ型の制約を満たすようにしたいと考えています。

たとえば、次のように記述できればよいでしょう。

string NormalizeLength(string value, int maxLength)
{
    return value.Substring(0, maxLength);
}

残念ながら、maxLength一般的に文字列の境界を超えているため、例外が発生しますvalue。もちろん、次のような関数を書くこともできますが、このようなものがすでに存在することを期待していました。

string NormalizeLength(string value, int maxLength)
{
    return value.Length <= maxLength ? value : value.Substring(0, maxLength);
} 

このタスクを実行するわかりにくいAPIはどこにありますか?ありますか?


24
レコードの場合、文字列は不変であり、切り捨てることはできません。切り捨てたコピーのみを返すことができます。ニッピッキー、知ってるよ。
John Weldon、

2
@John Weldon:それがおそらくメンバー関数が存在しない理由です-データ型のセマンティクスに従っていません。サイドノートでは、StringBuilderあなたは長さをshorterningで切り捨てることができますが、あなたはまだ文字列を広げる避けるために、長さチェックを実行する必要があります。
スティーブギディ

1
どちらの方法を選択する場合でも、Substringを呼び出したり、Lengthプロパティにアクセスしたりする前に、必ずnull文字列のチェックを追加してください。
レイ

3
@SteveGuidi-その場合、同様の意味上の問題に直面するTrimやReplaceなどの関数はありません
Chris Rogers

1
それが起こるとして、Microsoftよりもnitpicky @JohnWeldon自体が一貫し、ある-彼らは、たとえば、文書にしている幸せ、.Trim()それは誤解を招くことに変異のような文字列を音になりの方法では:「からすべての先頭と末尾の空白文字を削除します。現在の文字列オブジェクト。」
Mark Amery

回答:


620

Truncate()残念ながら、文字列にメソッドはありません。この種のロジックは自分で書く必要があります。ただし、これを拡張メソッドでラップすることで、どこにでも複製する必要がなくなります。

public static class StringExt
{
    public static string Truncate(this string value, int maxLength)
    {
        if (string.IsNullOrEmpty(value)) return value;
        return value.Length <= maxLength ? value : value.Substring(0, maxLength); 
    }
}

今、私たちは書くことができます:

var someString = "...";
someString = someString.Truncate(2);

5
素晴らしいソリューションですが、これはNET 3.5以降でのみ機能することを思い出してください。NET2.0で試さないでください。
ジェダイマスタースプーキー

7
VS 2008、そしておそらくVS 2010を使用している限り、.Net 2.0をターゲットにしている場合でも、これを行うことができます。danielmoth.com/Blog/...
マーク・

4
maxLengthが負の値の場合、これは失敗します。
Bernard

42
@ Bernard、maxLengthが負の場合、これは失敗するはずです。その他の動作は予期しないものになります。
bojingo 2014

12
null値に対して拡張メソッドを呼び出すことができます。
Joel Malone

127

または、三項演算子の代わりに、Math.minを使用できます

public static class StringExt
{
    public static string Truncate( this string value, int maxLength )
    {
        if (string.IsNullOrEmpty(value)) { return value; }

        return value.Substring(0, Math.Min(value.Length, maxLength));
    }
}

10
賢い!次の式は、元の文字列への参照を返すように最適化されていますvalue.Substring(0, value.Length)
スティーブギディ

4
残念ながら、value.LengthがMaxLength未満の場合に最適化されていません。これは、一部のデータでは一般的なケースです。また、文字列のLengthプロパティは大文字にする必要があります。
jpierson 2012

1
maxLengthが負の値の場合、これは失敗します。
Bernard

7
@バーナード、それでフレームワークの多くのものになるでしょう...しかし、私がそれをチェックすると...私はデフォルトにmaxLengthする0value.Length、または または私はスローする必要がありArgumentOutOfRangeExceptionます...これはこの場合より意味があり、Substringとにかくすでにスローされています。
CaffGeek、2015

2
少し短い:return string.IsNullOrEmpty(value) ? value : value.Substring(0, Math.Min(value.Length, maxLength));
user1127860

43

他の人が触れたすべてのケースをカバーしていて、まだ読みやすい簡潔な方法でカバーしていると信じているので、自分の実装を投入すると思いました。

public static string Truncate(this string value, int maxLength)
{
    if (!string.IsNullOrEmpty(value) && value.Length > maxLength)
    {
        return value.Substring(0, maxLength);
    }

    return value;
}

このソリューションは主にレイのソリューションに基づいて構築されており、LBushkinが彼のソリューションで行うのと同じようにthisキーワードを使用することにより、拡張メソッドとして使用するためのメソッドを開きます。


maxLengthが負の値の場合、これは失敗します。
Bernard

15
@Bernard-予期しない値であるため、maxLength引数に負の値を渡さないことをお勧めします。Substringメソッドも同じアプローチを取るため、スローする例外を改善する理由はありません。
jpierson 2013年

IsNullOrEmptyチェックは必要ないと思いますか?(1)値がnullの場合、この拡張メソッドを呼び出す方法はありません。(2)valueが空の文字列の場合、value.Length> maxLengthチェックは失敗します。
Jon Schneider 2015年

8
@JonSchneider、これは拡張メソッドであるため、IsNullOrEmptyが必要です。nullが割り当てられている文字列型の変数がある場合、コンパイラはこのメソッドを呼び出す前にnullチェックを挿入しません。技術的には、これはまだ静的クラスの静的メソッドです。つまり、stringVar.Truncate(2)は次のようにコンパイルされます。StringExt.Truncate(stringVar、2);
ジェフB

40

パフォーマンステストは楽しいので(linqpad拡張メソッドを使用)

var val = string.Concat(Enumerable.Range(0, 50).Select(i => i % 10));

foreach(var limit in new[] { 10, 25, 44, 64 })
    new Perf<string> {
        { "newstring" + limit, n => new string(val.Take(limit).ToArray()) },
        { "concat" + limit, n => string.Concat(val.Take(limit)) },
        { "truncate" + limit, n => val.Substring(0, Math.Min(val.Length, limit)) },
        { "smart-trunc" + limit, n => val.Length <= limit ? val : val.Substring(0, limit) },
        { "stringbuilder" + limit, n => new StringBuilder(val, 0, Math.Min(val.Length, limit), limit).ToString() },
    }.Vs();

このtruncate方法は「大幅に」高速でした。#microoptimization

早い

  • truncate10 5788ティックの経過(0.5788ミリ秒)[10K反復で、5.788E-05ミリ秒あたり]
  • smart-trunc10 8206ティックの経過(0.8206ミリ秒)[10K反復で、8.206E-05ミリ秒あたり]
  • stringbuilder10 10557ティックの経過(1.0557ミリ秒)[1万レップ、0.00010557ミリ秒あたり]
  • concat10 45495ティックの経過(4.5495ミリ秒)[1万回で、0.00045495ミリ秒あたり]
  • newstring10 72535ティックの経過(7.2535ミリ秒)[10K反復で、0.00072535ミリ秒あたり]

遅い

  • truncate44 8835ティックの経過(0.8835ミリ秒)[1万回で8.835E-05ミリ秒]
  • stringbuilder44 13106ティックの経過(1.3106ミリ秒)[1万レップ、0.00013106ミリ秒あたり]
  • smart-trunc44 14821ティックの経過(1.4821ミリ秒)[10K反復で、0.00014821ミリ秒あたり]
  • newstring44 144324ティックの経過(14.4324ミリ秒)[1万レップ、0.00144324ミリ秒あたり]
  • concat44 174610ティックの経過(17​​.461ミリ秒)[10K反復で、0.0017461ミリ秒あたり]

長すぎる

  • smart-trunc64 6944ティックの経過(0.6944ミリ秒)[1万回で、6.944E-05ミリ秒あたり]
  • truncate64 7686ティックの経過(0.7686ミリ秒)[1万回で7.686E-05ミリ秒]
  • stringbuilder64 13314ティックの経過(1.3314ミリ秒)[1万レップ、0.00013314ミリ秒あたり]
  • newstring64 177481ティックの経過(17​​.7481ミリ秒)[10K反復で、0.00177481ミリ秒あたり]
  • concat64 241601ティックの経過(24.1601ミリ秒)[1万レップ、0.00241601ミリ秒あたり]

すべての有用なベンチマークをありがとう!...そしてリンクパッドは揺れる!
サンセットクエスト

linqpadがそれらのことを実行できることを気にしなかった
jefissu


27

LINQを使用できます...文字列の長さを確認する必要がなくなります。確かにおそらく最も効率的ではないかもしれませんが、それは楽しいです。

string result = string.Join("", value.Take(maxLength)); // .NET 4 Join

または

string result = new string(value.Take(maxLength).ToArray());

2
なぜこれは受け入れられた答えではないのですか?最も簡単なのは、維持/文書化する必要がある独自の拡張メソッドを作成するか、.Take
Don Cheadle

9
@mmcrae Linqの方がわかりやすいかもしれませんが、速度もかなり遅くなります。私のベンチマークでは、Linqの場合は400ミリ秒、Substringの場合は100万回の反復でわずか24ミリ秒です。
Hein AndreGrønnestad2017年

このソリューションは決して使用すべきではありません。上記の2つのコメントで述べたように、既存の文字列が最大長より大きくない場合でも、常にメモリが割り当てられます。また、それは非常に遅いです。
カマレイ

15

私はこのように一行で私の物を作りました

value = value.Length > 1000 ? value.Substring(0, 1000) : value;

2
-1; これは、受け入れられた回答にまだなかったものをまったく追加しません。
マークアメリー2018年

2
@markameryは、使用する必要があるときに記述および更新するコードが少ない短い代替手段です。気に入らない?使用しないでください
SeanMC 2018年

迅速、シンプル、そして高速。これは私が必要としたものです。ありがとう!
Peter

14

誰もまだこれを投稿していないようです:

public static class StringExt
{
    public static string Truncate(this string s, int maxLength)
    {
        return s != null && s.Length > maxLength ? s.Substring(0, maxLength) : s;
    }
}

&&演算子を使用すると、受け入れられた回答よりわずかに良くなります。


13

.NET Frameworkには、次のような文字列を切り捨てるAPIがあります。

Microsoft.VisualBasic.Strings.Left(string, int);

ただし、C#アプリでは、下位互換性が主な存在理由であるMicrosoft.VisualBasic.dllに依存するよりも、おそらく自分でロールする方がよいでしょう。


".NET FrameworkにはAPIがあります"と矛盾します。これはVB.NET APIです
Camilo Terevinto

9
@CamiloTerevinto-.NET Frameworkに同梱されているAPIであり、任意のマネージ言語から呼び出すことができます。
Joe

1
VB DLLには多くの優れた機能があります。なぜ多くのC#開発者がそれに反対しているのですか?
マイケルZ.

残念ながら、現在.NET Coreはサポートされていません。実際、Microsoft.VisualBasic.Strings.NET Coreのモジュール全体はかなり空です。
マークアメリー2018年

1
Joeのコメントには同意しますが、他の言語からVBに固有の何かを呼び出すのは適切ではないと思います。「VB DLL」にたくさんの良いものがあれば、共有場所に置いてみませんか?マイクロソフトがこれらのものを明日どうするかを誰が知っていますか?サポートや何かを停止します
。–カマレイ


6

私はこれが古い質問であることを知っていますが、ここに素晴らしい解決策があります:

public static string Truncate(this string text, int maxLength, string suffix = "...")
{
    string str = text;
    if (maxLength > 0)
    {
        int length = maxLength - suffix.Length;
        if (length <= 0)
        {
            return str;
        }
        if ((text != null) && (text.Length > maxLength))
        {
            return (text.Substring(0, length).TrimEnd(new char[0]) + suffix);
        }
    }
    return str;
}

var myString = "hello world"
var myTruncatedString = myString.Truncate(4);

戻り値:こんにちは...


@SarjanWebDevその特殊文字は「。」として表示されます。cmd.exe
Neal Ehardt、2015年

5

C#6のNull伝播演算子を持つ同様のバリアント

public static string Truncate(this string value, int maxLength)
{
    return value?.Length <= maxLength ? value : value?.Substring(0, maxLength);
}

valueここでは、null かどうかを本質的に2回チェックしています。


5

C#文字列用の2016年のTruncateメソッドはまだありません。しかし-C#6.0構文を使用:

public static class StringExtension
{
  public static string Truncate(this string s, int max) 
  { 
    return s?.Length > max ? s.Substring(0, max) : s ?? throw new ArgumentNullException(s); 
  }
}

それは魅力のように機能します:

"Truncate me".Truncate(8);
Result: "Truncate"

4

@CaffGeekを取得して単純化する:

public static string Truncate(this string value, int maxLength)
    {
        return string.IsNullOrEmpty(value) ? value : value.Substring(0, Math.Min(value.Length, maxLength));
    }

4

文字列を切り捨てるとは、指定された長さだけで文字列を切り取るだけではなく、単語を分割しないように注意する必要があることに注意してください。

例:string:これはテスト文字列です。

11時にカットしたいです。上記のいずれかの方法を使用すると、結果は次のようになります。

これはテです

これは私たちが望むものではありません

私が使用している方法もそれほど完璧ではないかもしれませんが、ほとんどの状況を処理できます

public string CutString(string source, int length)
{
        if (source== null || source.Length < length)
        {
            return source;
        }
        int nextSpace = source.LastIndexOf(" ", length);
        return string.Format("{0}...", input.Substring(0, (nextSpace > 0) ? nextSpace : length).Trim());
} 

4

何故なの:

string NormalizeLength(string value, int maxLength)
{
    //check String.IsNullOrEmpty(value) and act on it. 
    return value.PadRight(maxLength).Substring(0, maxLength);
}

つまりvalue.Length < maxLength、最後にスペースを埋めるか、余分な部分を切り捨てる場合です。


2倍の数の文字列オブジェクトを生成すると、値がnullで不適切な場合にPadRight呼び出しからNullReferenceExceptionがスローされる可能性があり、ArgumentNullExceptionである必要があります。
ジェレミー

1
@Jeremy理解できません。「値がnullの場合、PadRight呼び出しからNullReferenceExceptionがスローされる可能性があります」; 「// string.IsNullOrEmpty(value)をチェックしてそれに基づいて動作する」とは言及していません。
2016

3

ここに十分な答えがない場合に備えて、これは私のものです:)

public static string Truncate(this string str, 
                              int totalLength, 
                              string truncationIndicator = "")
{
    if (string.IsNullOrEmpty(str) || str.Length < totalLength) 
        return str;

    return str.Substring(0, totalLength - truncationIndicator.Length) 
           + truncationIndicator;
}

使用する:

"I use it like this".Truncate(5,"~")

2

(過度に)複雑にするために、最後の3文字をmaxLengthパラメーターに関して省略記号で置き換える、オーバーロードされたバージョンを追加します。

public static string Truncate(this string value, int maxLength, bool replaceTruncatedCharWithEllipsis = false)
{
    if (replaceTruncatedCharWithEllipsis && maxLength <= 3)
        throw new ArgumentOutOfRangeException("maxLength",
            "maxLength should be greater than three when replacing with an ellipsis.");

    if (String.IsNullOrWhiteSpace(value)) 
        return String.Empty;

    if (replaceTruncatedCharWithEllipsis &&
        value.Length > maxLength)
    {
        return value.Substring(0, maxLength - 3) + "...";
    }

    return value.Substring(0, Math.Min(value.Length, maxLength)); 
}


1

私はjpiersonの答えを好みますが、ここで確認できる例では、maxLength <0の場合など、無効なmaxLengthパラメータを処理していません。

選択肢は、try / catchでエラーを処理するか、maxLengthパラメータminを0にクランプするか、maxLengthが0未満の場合は空の文字列を返します。

最適化されていないコード:

public string Truncate(this string value, int maximumLength)
{
    if (string.IsNullOrEmpty(value) == true) { return value; }
    if (maximumLen < 0) { return String.Empty; }
    if (value.Length > maximumLength) { return value.Substring(0, maximumLength); }
    return value;
}

3
私の実装ではmaximumLengthが0未満の場合を処理しないことを選択したことに注意してください。私が行う唯一のことは、本質的にstring.Substring()が実行するArgumentOutOfRangeExcpetionをスローすることでした。
jpierson 2013年

1

ここにvb.netソリューションがあります。文字列が既にmaxlengthよりも小さい場合はsubstringステートメントが必要ないため、if(醜いですが)ステートメントがパフォーマンスを向上させることをマークします...これを文字列の拡張にすることで、使いやすくなります。 ..

 <System.Runtime.CompilerServices.Extension()> _
    Public Function Truncate(String__1 As String, maxlength As Integer) As String
        If Not String.IsNullOrEmpty(String__1) AndAlso String__1.Length > maxlength Then
            Return String__1.Substring(0, maxlength)
        Else
            Return String__1
        End If
    End Function

VB.netでは、「Not String.IsNullOrEmpty(String__1)」を「String__1 <> Nothing」に置き換えることができます。少し短いです。文字列のデフォルト値は空の文字列です。「<> Nothing」を使用すると、nullと空の文字列の両方がチェックされます。Truncate( ""、50)とTruncate(Nothing、50)で
テストしてください

VBでは、あなたは左(文字列、MAXLENGTH)行うことができます
マイケル・Z.

1

私はすでにたくさんの回答があることを知っていますが、私の必要性は、文字列の最初と最後をそのままにして、最大長未満に短くすることでした。

    public static string TruncateMiddle(string source)
    {
        if (String.IsNullOrWhiteSpace(source) || source.Length < 260) 
            return source;

        return string.Format("{0}...{1}", 
            source.Substring(0, 235),
            source.Substring(source.Length - 20));
    }

これは、最大長が260文字のSharePoint URLを作成するためのものです。

定数260であるため、長さをパラメーターにしませんでした。最初の部分文字列の長さをパラメーターにしませんでした。これは、特定のポイントで中断させたいためです。最後に、2番目の部分文字列はソースの長さです-私はフォルダー構造を知っているので20です。

これは、特定のニーズに簡単に適合させることができます。


1

私はすでにここにたくさんの答えがあることを知っていますが、これは私が行ったもので、ヌル文字列と渡された長さが負の状況の両方を処理します:

public static string Truncate(this string s, int length)
{
    return string.IsNullOrEmpty(s) || s.Length <= length ? s 
        : length <= 0 ? string.Empty 
        : s.Substring(0, length);
}


0

これについて.netには私が知っていることは何もありません-ここに「...」を追加した私のバージョンがあります:

public static string truncateString(string originalString, int length) {
  if (string.IsNullOrEmpty(originalString)) {
   return originalString;
  }
  if (originalString.Length > length) {
   return originalString.Substring(0, length) + "...";
  }
  else {
   return originalString;
  }
}

2
ご使用のバージョンでは、切り捨てられた場合に備えて、要求された長さより3文字長い文字列が提供されます。その上、トリプルドットは表現で本当に意味があるだけなので、OPが提供したユースケースのようなデータベースには保存しません。
MarioDS 2014年

0

TruncateString

public static string _TruncateString(string input, int charaterlimit)
{
    int characterLimit = charaterlimit;
    string output = input;

    // Check if the string is longer than the allowed amount
    // otherwise do nothing
    if (output.Length > characterLimit && characterLimit > 0)
    {
        // cut the string down to the maximum number of characters
        output = output.Substring(0, characterLimit);
        // Check if the character right after the truncate point was a space
        // if not, we are in the middle of a word and need to remove the rest of it
        if (input.Substring(output.Length, 1) != " ")
        {
            int LastSpace = output.LastIndexOf(" ");

            // if we found a space then, cut back to that space
            if (LastSpace != -1)
            {
                output = output.Substring(0, LastSpace);
            }
        }
        // Finally, add the "..."
        output += "...";
    }
    return output;
}

2
パブリックメソッド名の前にアンダースコアを付けるのはなぜですか?
マイケルZ.

0

上記の可能性に加えて、私の解決策を共有したいと思います。これは、nullを許可する拡張メソッドで(string.Emptyを返します)、省略記号とともに使用するための2つ目の.Truncate()もあります。注意してください、それはパフォーマンスが最適化されていません。

public static string Truncate(this string value, int maxLength) =>
    (value ?? string.Empty).Substring(0, (value?.Length ?? 0) <= (maxLength < 0 ? 0 : maxLength) ? (value?.Length ?? 0) : (maxLength < 0 ? 0 : maxLength));
public static string Truncate(this string value, int maxLength, string ellipsis) =>
    string.Concat(value.Truncate(maxLength - (((value?.Length ?? 0) > maxLength ? ellipsis : null)?.Length ?? 0)), ((value?.Length ?? 0) > maxLength ? ellipsis : null)).Truncate(maxLength);

-1
public static string Truncate( this string value, int maxLength )
    {
        if (string.IsNullOrEmpty(value)) { return value; }

        return new string(value.Take(maxLength).ToArray());// use LINQ and be happy
    }

ToArray()ここでの呼び出しは、不必要なオーバーヘッドです。たとえばString.Concat、配列を経由することなく、列挙可能な文字から文字列を作成できます。
マークアメリー2018年

-3

文字列を切り捨て

public static string TruncateText(string strText, int intLength)
{
    if (!(string.IsNullOrEmpty(strText)))
    {                                
        // split the text.
        var words = strText.Split(' ');

        // calculate the number of words
        // based on the provided characters length 
        // use an average of 7.6 chars per word.
        int wordLength = Convert.ToInt32(Math.Ceiling(intLength / 7.6));

        // if the text is shorter than the length,
        // display the text without changing it.
        if (words.Length <= wordLength)
            return strText.Trim();                

        // put together a shorter text
        // based on the number of words
        return string.Join(" ", words.Take(wordLength)) + " ...".Trim();
    }
        else
        {
            return "";
        }            
    }

これはOPの質問には答えません。まず、それはメンバー関数である必要があります(ただし、拡張メソッドとして作成しました)。第2に、OPはテキストを分割する必要があることを指定しておらず、単語は約に切り捨てられています。単語あたり7.6文字。
Wicher Visser

7.6は単なる数値です。あなたが望む他の番号を書くことができます。これはたまたま平均的な英語の単語の長さです。私はそれをグーグルで見つけた。分割を使用すると、単語をスペースで分解する簡単な方法になります。半角表示したくないと思います!したがって、ループして空のスペースを見つけてコードを追加する必要がない限り、これは文字列を切り捨てて完全な単語を表示する簡単な方法です。これにより、文字列が指定された長さより長くなく、単語が壊れることはありません。
VT

-4

これは私が通常使用するコードです:

string getSubString(string value, int index, int length)
        {
            if (string.IsNullOrEmpty(value) || value.Length <= length)
            {
                return value;
            }
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            for (int i = index; i < length; i++)
            {
                sb.AppendLine(value[i].ToString());
            }
            return sb.ToString();
        }

5
文字列を+ =で連結すると、特に文字ごとに再構築する場合に負荷の高い操作になることに注意してください。.NET文字列は不変です。つまり、この場合、ループのたびに新しい文字列が作成されます。
スティーブギディ2014

@SteveGuidi文字列は不変ではなく、不変であるかのように見せかけます。文字列が文字列と文字列を持つことができるように文字列が真の不変のプリミティブであることを望みますが、残念ながらそれらはプリミティブではありません。
Chris Marisic

パフォーマンスコストが大きいかのように高価だと言ったので、stringBuilderを使用するように変更しましたが、+ =を使用すると何が起こっているのかを簡単に確認できるので、OPにコードを簡単に理解させたいだけです。
user3390116 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.