Как обрезать строку .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
Для записи, строки неизменны, вы не можете их усекать, вы можете только вернуть их усеченную копию. Nitpicky, я знаю.
— Джон Уэлдон

2
@ Джон Уэлдон: Вероятно, поэтому функция-член не существует - она ​​не соответствует семантике типа данных. Что StringBuilderкасается примечания, позволяет обрезать путем сокращения длины, но вам все равно необходимо выполнить проверку длины, чтобы избежать расширения строки.
— Стив Гуиди

1
Какое бы решение вы ни выбрали, обязательно добавьте проверку на пустую строку перед вызовом Substring или доступом к свойству Length.
— Рэй

3
@SteveGuidi - Если бы это было так, то не было бы таких функций, как Trim или Replace, которые сталкивались бы с подобными семантическими проблемами
— Крис Роджерс

1
@JohnWeldon Более придирчивы, чем сами Microsoft, как это бывает - они счастливы документировать, например, .Trim()таким образом, что это вводит в заблуждение, как будто мутирует строку: «Удаляет все начальные и конечные пробельные символы из текущий объект String. "
— Марк Амери

Ответы:


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.
— Мастер-джедай Spooky

7
Пока вы находитесь в VS 2008 и, предположительно, в VS 2010, вы все равно могли бы делать это, даже ориентируясь на .Net 2.0. danielmoth.com/Blog/...
— Mark

4
Это не удастся, когда maxLengthотрицательное значение.
— Бернард

42
@ Бернард, это должно произойти, если maxLength отрицателен. Любое другое поведение будет неожиданным.
— Bojingo

12
Вы можете вызывать методы расширения для нулевых значений.
— Джоэл Мэлоун

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

1
Это не удастся, когда maxLengthотрицательное значение.
— Бернард

7
@Bernard, так что будет много вещей , в рамках ... но если я проверить ... Я либо есть по умолчанию , maxLengthчтобы 0или value.Length; или мне нужно бросить ArgumentOutOfRangeException..., который имеет больше смысла в этом случае, и все равно уже брошен Substring.
— CaffGeek

2
Немного короче:return string.IsNullOrEmpty(value) ? value : value.Substring(0, Math.Min(value.Length, maxLength));
— user1127860 13.09.16

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 делает в своем решении.


Это не удастся, когда maxLengthотрицательное значение.
— Бернард

15
@Bernard - я бы рекомендовал не передавать отрицательное значение для аргумента maxLength, так как это неожиданное значение. Метод Substring использует тот же подход, поэтому нет причин улучшать исключение, которое он выдает.
— jpierson

Я не думаю, что проверка IsNullOrEmpty необходима? (1) Если значение равно нулю, у него не должно быть способа вызвать этот метод расширения. (2) Если значение является пустой строкой, проверка value.Length> maxLength завершится неудачей.
— Джон Шнайдер

8
@JonSchneider, IsNullOrEmpty требуется, потому что это метод расширения. Если у вас есть переменная типа string, которой присвоено значение NULL, компилятор не вставляет проверку NULL перед вызовом этого метода. Технически, это все еще статический метод статического класса. Итак: stringVar.Truncate (2) Компилируется как: StringExt.Truncate (stringVar, 2);
— Джефф Б

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 мс) [за 10 000 повторений, 5,788E-05 мс за]
  • smart-trunc10 истекло 8206 тактов (0,8206 мс) [в 10 000 повторений, 8,206E-05 мс за]
  • stringbuilder10 Прошло 10557 тактов (1,0557 мс) [за 10 000 повторений, 0,00010557 мс за]
  • concat10 Прошло 45495 тактов (4,5495 мс) [в 10 000 повторений, 0,00045495 мс за]
  • newstring10 72535 тактов прошло (7,2535 мс) [в 10 000 повторений, 0,00072535 мс на]

поздно

  • truncate44 Прошло 8835 тактов (0,8835 мс) [в 10 000 повторений, 8,835E-05 мс за]
  • stringbuilder44 Прошло 13106 тактов (1,3106 мс) [за 10 000 повторений, 0,00013106 мс за]
  • smart-trunc44 Прошло 14821 тактов (1,4821 мс) [в 10 000 повторений, 0,00014821 мс за]
  • newstring44 144324 тактов прошло (14,4324 мс) [за 10 000 повторений, 0,00144324 мс за]
  • concat44 Прошло 174610 тиков (17,461 мс) [за 10 тысяч повторений, 0,0017461 мс за]

Слишком долго

  • smart-trunc64 Прошло 6944 такта (0,6944 мс) [за 10 000 повторений, 6,944E-05 мс за]
  • truncate64 Прошло 7686 тактов (0,7686 мс) [за 10 000 повторений, 7,686E-05 мс за]
  • stringbuilder64 Прошло 13314 тактов (1,3314 мс) [в 10 000 повторений, 0,00013314 мс за]
  • newstring64 177481 тактов прошло (17.7481 мс) [за 10 000 повторений, 0.00177481 мс за]
  • concat64 Прошло 241601 тактов (24,1601 мс) [за 10 000 повторений, 0,00241601 мс за]

Спасибо за все полезные тесты! ... и Linkpad качается!
— Sunsetquest

никогда не думал, что linqpad может делать такие вещи
— jefissu


27

Вы можете использовать LINQ ... это избавляет от необходимости проверять длину строки. Правда, возможно, не самый эффективный, но это весело.

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

или

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

2
почему это не принятый ответ? Что самое простое, написать свой собственный метод Extension, который вам нужно поддерживать / документировать, или использовать что-то, как BUILT IN, например .Take
— Don Cheadle

9
@mmcrae Linq может быть более прямым, но и намного медленнее. Мой тест говорит о ~ 400 мс для Linq и всего ~ 24 мс для подстроки на 1 миллион итераций.
— Hein Andre Grønnestad

Это решение никогда не должно использоваться. Как сказано в двух вышеупомянутых комментариях, всегда есть выделение памяти, даже когда существующая строка не превышает максимальную длину. Также это очень медленно.
— Камари

15

Я сделал мой в одной строке вроде этого

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

2
-1; это не добавляет ничего, чего не было в принятом ответе.
— Марк Эмери

2
@markamery - это более короткая альтернатива с меньшим количеством кода для написания и обновления, когда вам нужно его использовать. Не нравится это? Не используйте его
— SeanMC

Быстро, просто и быстро. Это то, что мне было нужно. Спасибо!
— Питер

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», вы сами себе противоречите. Это API VB.NET
— Камило Теревинто,

9
@CamiloTerevinto - это API, который поставляется с .NET Framework и может вызываться из любого управляемого языка.
— Джо

1
В VB DLL есть много хорошего. Почему так много разработчиков C # против?
— Майкл З.

К сожалению, в настоящее время нет поддержки .NET Core. Действительно, все Microsoft.VisualBasic.Stringsмодули в .NET Core довольно пусты .
— Марк Амери

1
Хотя я согласен с комментарием Джо, все же я не чувствую себя правильным, называя что-то конкретное для VB с других языков. Если в «VB DLL» так много хороших вещей, почему бы не поместить их в какое-то общее место? Кто знает, что Microsoft будет делать с этими вещами завтра? Прекратить поддержку или что-то ..
— Kamarey


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
— Нил Эхардт

5

Аналогичный вариант с оператором распространения C # 6 Null

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

Пожалуйста, обратите внимание, что мы по существу проверяем, valueявляется ли значение null здесь дважды.


5

По-прежнему нет метода усечения в 2016 году для строк C #. Но - используя синтаксис 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

Обратите внимание, что усечение строки не просто означает просто обрезку строки только на заданную длину, но должно заботиться о том, чтобы не разбить слово.

например, строка: это тестовая строка.

Я хочу сократить это в 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пробелах места для пробелов до конца или усекать лишнее.


Вы генерируете в два раза больше строковых объектов, и это может вызвать исключение NullReferenceException из вызова PadRight, если значение равно null, что неуместно, это должно быть ArgumentNullException.
— Джереми

1
@ Джереми, я не понимаю, «это может вызвать исключение NullReferenceException из вызова PadRight, если значение равно нулю»; Разве я не упомянул "// проверить строку. IsNullOrEmpty (значение) и действовать на него".
— Sri

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)); 
}

2

Мои два цента с примером длины 30:

  var truncatedInput = string.IsNullOrEmpty(input) ? 
      string.Empty : 
      input.Substring(0, Math.Min(input.Length, 30));

1

Я предпочитаю ответ jpierson, но ни один из приведенных здесь примеров не обрабатывает недопустимый параметр maxLength, например, когда maxLength <0.

Выбор будет либо обрабатывать ошибку в 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, потому что я решил, что единственное, что я хотел бы сделать, это бросить ArgumentOutOfRangeExcpetion, который, по сути, делает для меня string.Substring ().
— jpierson

1

Вот решение vb.net, отметьте, что оператор if (хотя и уродливый) повышает производительность, потому что нам не нужен оператор substring, когда строка уже меньше maxlength ... Делая это расширением строки, его легко использовать. ..

 <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» проверяет как нулевые, так и пустые строковые значения. Проверьте это с помощью: усечения ("", 50) и усечения (ничего, 50)
— jrjensen

В VB вы можете сделать левый (строка, максимальная длина)
— Майкл З.

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));
    }

Это предназначено для создания URL-адресов SharePoint длиной не более 260 символов.

Я не сделал длину параметром, так как она является постоянной величиной 260. Я также не сделал первую длину подстроки параметром, потому что я хочу, чтобы она ломалась в определенной точке. Наконец, вторая подстрока - это длина источника - 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);
}

1

В C # 8 может быть использована новая функция диапазонов ...

value = value[..Math.Min(30, value.Length)];

0

В этом нет ничего, что мне известно, - вот моя версия, которая добавляет «...»:

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

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
Почему перед вашим публичным именем метода стоит знак подчеркивания?
— Майкл З.

0

В дополнение к рассмотренным выше возможностям я хотел бы поделиться своим решением. Это метод расширения, который позволяет null (возвращает string.Empty), а также есть второй .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вы можете создать строку из множества символов без необходимости проходить через массив.
— Марк Эмери

-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 не указывает, что текст должен быть разделен, а слова должны быть усечены до приблиз. 7,6 символа за слово.
— Wicher Visser

7,6 это просто число. Вы можете написать любой другой номер, который вы хотите. Это оказалось средней длины английского слова. Я нашел это в Google. Использование split - это простой способ разбить слова на пробелы. Я не думаю, что вы хотите отобразить половину слова! Таким образом, если вы не перебираете, чтобы найти пустое пространство, которое потребует больше кода, это простой способ обрезать строку и отображать полные слова. Это гарантирует, что строка не длиннее заданной длины, и у вас не будет разбитых слов.
— 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 являются неизменяемыми, что означает, что в этом случае новая строка создается каждый раз в вашем цикле.
— Стив Гуиди,

Строки @SteveGuidi не являются неизменяемыми, они просто маскируются как неизменные. Я бы хотел, чтобы строки были настоящими неизменяемыми примитивами, чтобы я мог иметь строку и строку ?, но, увы, они не являются примитивами.
— Крис Марисик

Вы говорите «дорого», как будто затраты на производительность значительны, я изменил его на использование stringBuilder, но я обнаружил, что с + = легче увидеть, что происходит, я просто хотел, чтобы OP легко понимал код.
— user3390116
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.