Como truncar uma string .NET?


406

Gostaria de truncar uma string de forma que seu comprimento não exceda um determinado valor. Estou escrevendo em uma tabela de banco de dados e quero garantir que os valores que eu escrevo atendam às restrições do tipo de dados da coluna.

Por exemplo, seria bom se eu pudesse escrever o seguinte:

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

Infelizmente, isso gera uma exceção, porque maxLengthgeralmente excede os limites da string value. Claro, eu poderia escrever uma função como a seguinte, mas esperava que algo assim já existisse.

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

Onde está a API indescritível que executa essa tarefa? Existe um?


24
Para o registro, as strings são imutáveis, você não pode truncá-las, você só pode retornar uma cópia truncada delas. Nitpicky, eu sei.
— John Weldon

2
@ John Weldon: Provavelmente é por isso que a função de membro não existe - ela não segue a semântica do tipo de dados. Em uma nota lateral, StringBuilderpermite truncar encurtando o comprimento, mas você ainda precisa executar a verificação do comprimento para evitar o alargamento da corda.
— Steve Guidi 5/05

11
Qualquer que seja a solução escolhida, adicione uma verificação de uma cadeia nula antes de chamar Substring ou acessar a propriedade Length.
— Ray

3
@SteveGuidi - Se fosse esse o caso, então não haveria funções como guarnição ou Substituir, que enfrentam problemas semânticos semelhantes
— Chris Rogers

11
@JohnWeldon Mais detalhista do que a própria Microsoft, consistentemente, é o que acontece - eles ficam felizes em documentar, por exemplo, .Trim()de uma maneira que pareça enganosa, pois muda a string: "Remove todos os caracteres de espaço em branco iniciais e finais do objeto String atual. "
— Mark Amery

Respostas:


620

Truncate()Infelizmente, não há um método na string. Você precisa escrever esse tipo de lógica. O que você pode fazer, no entanto, é agrupar isso em um método de extensão para que você não precise duplicá-lo em qualquer lugar:

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

Agora podemos escrever:

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

5
Ótima solução, mas lembre-se de que isso só funciona no NET 3.5 ou superior. Não tente no NET2.0.
— Jedi Master Spooky

7
Enquanto você estiver no VS 2008 e, presumivelmente, no VS 2010, ainda poderá fazer isso, mesmo tendo como alvo o .Net 2.0. danielmoth.com/Blog/...
— Mark

4
Isso falhará quando maxLengthfor um valor negativo.
— 22713 Bernard Bernard

42
@ Bernard, isso deve falhar se maxLength for negativo. Qualquer outro comportamento seria inesperado.
— bojingo

12
Você pode chamar métodos de extensão com valores nulos.
— Joel Malone

127

Ou, em vez do operador ternário, você pode usar 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
Inteligente! E a seguinte expressão é otimizado para retornar uma referência ao string original: value.Substring(0, value.Length).
— Steve Guidi

4
Infelizmente, não é otimizado para casos em que value.Length é menor que MaxLength, o que pode ser um caso comum em alguns dados. Também a propriedade Length na string deve ser maiúscula.
— jpierson

11
Isso falhará quando maxLengthfor um valor negativo.
— 27513 Bernard Bernard

7
@ Bernard, o mesmo acontecerá com muitas coisas no quadro ... mas se eu verificar ... eu tenho que usar maxLengthcomo padrão 0ou value.Length; ou eu preciso jogar um ArgumentOutOfRangeException... o que faz mais sentido nesse caso, e já é jogado de Substringqualquer maneira.
— CaffGeek 3/15/13

2
Um pouco mais curto:return string.IsNullOrEmpty(value) ? value : value.Substring(0, Math.Min(value.Length, maxLength));
— user1127860

43

Imaginei que lançaria minha implementação, pois acredito que ela cobre todos os casos que foram abordados pelos outros e o faz de uma maneira concisa que ainda é legível.

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

    return value;
}

Essa solução baseia-se principalmente na solução de Ray e abre o método para uso como método de extensão, usando a palavra - chave this, assim como LBushkin em sua solução.


Isso falhará quando maxLengthfor um valor negativo.
— 27513 Bernard Bernard

15
@ Bernard - Eu recomendaria não passar um valor negativo para o argumento maxLength, pois é um valor inesperado. O método Substring segue a mesma abordagem, portanto, não há razão para aprimorar a exceção lançada.
— jpierson

Não acho que a verificação IsNullOrEmpty seja necessária? (1) Se o valor for nulo, não deverá haver como chamar esse método de extensão. (2) Se value for a string vazia, a verificação value.Length> maxLength falhará.
— Jon Schneider

8
@ JonSchneider, o IsNullOrEmpty é necessário porque este é um método de extensão. Se você tiver uma variável do tipo string atribuída a um valor nulo, o compilador não insere uma verificação nula antes de chamar esse método. Tecnicamente, esse ainda é um método estático da classe estática. Então: stringVar.Truncate (2) Compila como: StringExt.Truncate (stringVar, 2);
— Jeff B

40

Como o teste de desempenho é divertido: (usando métodos de extensão 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();

O truncatemétodo foi "significativamente" mais rápido. #microoptimization

Cedo

  • truncate10 5788 carrapatos decorridos (0,5788 ms) [em 10K repetições, 5.788E-05 ms por]
  • ticks de 8206 smart-trunc10 decorridos (0,8206 ms) [em 10K repetições, 8,206E-05 ms por]
  • stringbuilder10 10557 pontos decorridos (1.0557 ms) [em 10K repetições, 0,00010557 ms por]
  • concat10 45495 tiques decorridos (4.5495 ms) [em 10K repetições, 0,00045495 ms por]
  • newstring10 72535 tiques decorridos (7,2535 ms) [em 10K repetições, 0,00072535 ms por]

Atrasado

  • truncate44 8835 tiques decorridos (0,8835 ms) [em 10K repetições, 8,835E-05 ms por]
  • stringbuilder44 13106 pontos decorridos (1.3106 ms) [em 10K repetições, 0,00013106 ms por]
  • ticks 14821 smart-trunc44 decorridos (1,4821 ms) [em 10K repetições, 0,00014821 ms por]
  • newstring44 144324 pontos decorridos (14,4324 ms) [em 10K repetições, 0,00144324 ms por]
  • concat44 174610 tiques decorridos (17,461 ms) [em 10K repetições, 0,0017461 ms por]

Demasiado longo

  • tiquetaques smart-trunc64 6944 decorridos (0,6944 ms) [em 10K repetições, 6,944E-05 ms por]
  • truncate64 7686 tiques decorridos (0,7686 ms) [em 10K repetições, 7,686E-05 ms por]
  • stringbuilder64 13314 carrapatos decorridos (1,3314 ms) [em 10K repetições, 0,00013314 ms por]
  • newstring64 177481 tiques decorridos (17.7481 ms) [em 10K repetições, 0,00177481 ms por]
  • concat64 241601 ticks decorridos (24.1601 ms) [em 10K repetições, 0,00241601 ms por]

Obrigado por todos os benchmarks úteis! ... e o Linkpad arrasa!
— Sunsetquest

nunca se importou que LINQPad poderia fazer essas coisas
— jefissu

38

No .NET 4.0, você pode usar o Takemétodo:

string.Concat(myString.Take(maxLength));

Não testado quanto à eficiência!


27

Você pode usar o LINQ ... elimina a necessidade de verificar o comprimento da string. É certo que talvez não seja o mais eficiente, mas é divertido.

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

ou

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

2
por que essa não é a resposta aceita? O que é mais para a frente, escrever o seu próprio método de extensão que você precisa para manter / documento ou usando algo construído em como .Pegue
— Don Cheadle

9
@mmcrae Linq pode ser mais direto, mas também é muito mais lento. Meu benchmark diz ~ 400ms para Linq e apenas ~ 24ms para Substring por 1 milhão de iterações.
— Hein Andre Grønnestad

Esta solução nunca deve ser usada. Como dito nos dois comentários acima, sempre há alocação de memória, mesmo quando a string existente não é maior que o comprimento máximo. Também é muito lento.
— Kamarey 27/02

15

Eu fiz o meu em uma linha como esta

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

2
-1; isso não adiciona nada que ainda não estava na resposta aceita.
— Mark Amery

2
@markamery é uma alternativa mais curta, com menos código para escrever e atualizar quando você precisar usá-lo. Não gosta disso? Não usá-lo
— SeanMC

Rápido, simples e rápido. Era disso que eu precisava. Obrigado!
— Peter

14

Parece que ninguém postou isso ainda:

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

O uso do operador && o torna marginalmente melhor que a resposta aceita.


13

O .NET Framework possui uma API para truncar uma string como esta:

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

Mas, em um aplicativo C #, você provavelmente prefere usar o Microsoft.VisualBasic.dll, cuja principal razão de ser é a compatibilidade com versões anteriores.


"O .NET Framework tem uma API" você está se contradizendo. Essa é uma API do VB.NET
— Camilo Terevinto

9
@CamiloTerevinto - é uma API fornecida com o .NET Framework e pode ser chamada de qualquer idioma gerenciado.
— Joe

11
A DLL do VB contém muitas coisas boas. Por que existem tantos c # devs contra isso?
— Michael Z.

Infelizmente, não há suporte para o .NET Core. De fato, todos os Microsoft.VisualBasic.Stringsmódulos do .NET Core estão vazios .
— Mark Amery

11
Embora eu concorde com o comentário de Joe, ainda não me sinto bem chamando algo específico para VB de outros idiomas. Se houver tanta coisa boa na "DLL do VB", por que não colocá-la em algum lugar compartilhado? Quem sabe o que a Microsoft fará com essas coisas amanhã? Apoio parada ou algo ..
— Kamarey


6

Sei que essa é uma pergunta antiga, mas aqui está uma boa solução:

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

Retorna: olá ...


@SarjanWebDev Esse caractere especial aparece como "." em cmd.exe
— Neal Ehardt,

5

Uma variante semelhante ao operador de propagação nula do C # 6

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

Observe que estamos essencialmente verificando se valueé nulo duas vezes aqui.


5

Ainda não há método Truncar em 2016 para seqüências de caracteres C #. Mas - usando a sintaxe do 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); 
  }
}

Ele funciona como um encanto:

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

4

Tomando o @CaffGeek e simplificando-o:

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

4

Observe que truncar uma string não significa apenas cortar uma string apenas com um comprimento especificado, mas deve-se tomar cuidado para não dividir a palavra.

eg string: esta é uma string de teste.

Eu quero cortar às 11. Se usarmos algum dos métodos acima, o resultado será

isso é um te

Não é isso que queremos

O método que estou usando também pode não ser tão perfeito, mas pode lidar com a maior parte da situação

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

Por que não:

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

ou seja, nos value.Length < maxLengthespaços do bloco de eventos até o fim ou truncar o excesso.


Você gera o dobro de objetos de seqüência de caracteres e pode gerar uma NullReferenceException da chamada PadRight, se o valor for nulo, o que é inadequado, deve ser uma ArgumentNullException.
— Jeremy

11
@ Jeremy Eu não entendo "ele poderia lançar uma NullReferenceException da chamada PadRight se o valor for nulo"; eu não mencionei "// verifique a string.IsNullOrEmpty (value) e aja de acordo com ela."
— Sri

3

Apenas no caso de não haver respostas suficientes aqui, aqui está a minha :)

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

usar:

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

2

Por uma questão de (excesso) complexidade, adicionarei minha versão sobrecarregada que substitui os últimos 3 caracteres por uma elipse em relação ao parâmetro 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

Meus dois centavos com exemplo de comprimento de 30:

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

1

Prefiro a resposta de jpierson, mas nenhum dos exemplos aqui que posso ver está lidando com um parâmetro maxLength inválido, como quando maxLength <0.

As opções seriam manipular o erro em uma tentativa / captura, fixe o parâmetro maxLength min a 0 ou, se maxLength for menor que 0, retorne uma sequência vazia.

Código não otimizado:

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
Observe que, na minha implementação, optei por não lidar com o caso em que maximumLength é menor que 0 porque achei que a única coisa que faria seria lançar um ArgumentOutOfRangeExcpetion, que essencialmente o que string.Substring () faz por mim.
— jpierson

1

Aqui está uma solução vb.net, marque que a instrução if (embora feia) melhora o desempenho, porque não precisamos da instrução substring quando a string já é menor que o comprimento máximo ... Ao torná-la uma extensão da string, é fácil de usar. ..

 <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

No VB.net, você pode substituir "Not String.IsNullOrEmpty (String__1)" por "String__1 <> Nothing". É um pouco mais curto. O valor padrão para cadeias é uma cadeia vazia. O uso de "<> Nothing" verifica os casos de seqüência de caracteres nulos e vazios. Teste-o com: Truncar ("", 50) e Truncar (nada, 50)
— jrjensen 15/05

Em VB você pode fazer Esquerda (string, maxlength)
— Michael Z.

1

Eu sei que já existem muitas respostas, mas minha necessidade era manter intacto o início e o fim da string, mas encurtá-la para o comprimento máximo.

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

Isso é para criar URLs do SharePoint com um comprimento máximo de 260 caracteres.

Eu não tornei o comprimento um parâmetro, pois é uma constante 260. Também não fiz o primeiro comprimento da substring um parâmetro porque quero que ele quebre em um ponto específico. Finalmente, a segunda substring é o comprimento da fonte - 20 desde que conheço a estrutura da pasta.

Isso pode ser facilmente adaptado às suas necessidades específicas.


1

Eu sei que já existem muitas respostas aqui, mas essa é a que eu tenho, que lida com cadeias nulas e a situação em que o comprimento passado é negativo:

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

No C # 8, o novo recurso Ranges pode ser usado ...

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

0

Não há nada no .net para isso que eu saiba - aqui está a minha versão que adiciona "...":

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
Sua versão fornecerá seqüências de caracteres com três caracteres a mais que o tamanho solicitado, caso estejam truncadas. Além disso, os pontos triplos são realmente apenas significativos na representação, eu não os armazenaria em um banco de dados como aquele que é o caso de uso que o OP forneceu.
— 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
Por que você precede o nome do método público com um sublinhado?
— Michael Z.

0

Como complemento às possibilidades discutidas acima, eu gostaria de compartilhar minha solução. É um método de extensão que permite null (retorna string.Empty) e também existe um segundo .Truncate () para usá-lo com reticências. Cuidado, não é um desempenho otimizado.

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
    }

A ToArray()ligação aqui é apenas desnecessária; usando, por exemplo, String.Concatvocê pode construir uma string a partir de um enumerável de caracteres sem ter que passar por uma matriz.
— Mark Amery

-3

Truncar String

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

Isso não responde à pergunta do OP. Primeiro, deve ser uma função de membro (embora você a tenha escrito como um método de extensão). Segundo, o OP não especifica que o texto precise ser dividido e que as palavras sejam truncadas para aprox. 7,6 caracteres por palavra.
— Wicher Visser

7.6 é apenas um número. você pode escrever qualquer outro número que desejar. Por acaso, esse é o tamanho médio das palavras em inglês. Eu encontrei isso no google. Usar a divisão é apenas uma maneira fácil de dividir as palavras por espaço. Eu não acho que você queira exibir meia palavra! Portanto, a menos que você percorra para encontrar o espaço vazio que exigirá mais código, essa é uma maneira fácil de truncar uma string e exibir palavras completas. Isso garantirá que uma string não seja maior que o comprimento especificado e que você não terá palavras quebradas.
— VT

-4

Este é o código que eu costumo usar:

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
Observe que concatenar cadeias com + = é uma operação cara, especialmente ao reconstruir caractere por caractere. As strings do .NET são imutáveis, o que significa que, nesse caso, uma nova string é criada a cada vez em seu loop.
— Steve Guidi

As strings do @SteveGuidi não são imutáveis, apenas se disfarçam de imutáveis. Eu gostaria que as strings fossem verdadeiras primitivas imutáveis ​​para que eu pudesse ter string e string ?, mas, infelizmente, elas não são primitivas.
— Chris Marisic

Você diz caro, como se o custo de desempenho fosse significativo, eu mudei para usar stringBuilder, mas acho que com + = é mais fácil ver o que está acontecendo, eu só queria que o OP entendesse facilmente o código.
— user3390116
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.