2010-08-17 5 views
10

Je tente de créer une méthode d'extension de chaîne pour rogner une chaîne sur une certaine longueur, mais sans casser un mot. Je voulais vérifier s'il y avait quelque chose intégré dans le cadre ou une méthode plus intelligente que la mienne. Voici le mien jusqu'à présent (pas entièrement testé):Création d'une fonction de rognage de chaîne intelligente en C#

public static string SmartTrim(this string s, int length) 
     { 
      StringBuilder result = new StringBuilder(); 

      if (length >= 0) 
      { 
       if (s.IndexOf(' ') > 0) 
       { 
        string[] words = s.Split(' '); 
        int index = 0; 

        while (index < words.Length - 1 && result.Length + words[index + 1].Length <= length) 
        { 
         result.Append(words[index]); 
         result.Append(" "); 
         index++; 
        } 

        if (result.Length > 0) 
        { 
         result.Remove(result.Length - 1, 1); 
        } 
       } 
       else 
       { 
        result.Append(s.Substring(0, length)); 
       } 
      } 
      else 
      { 
       throw new ArgumentOutOfRangeException("length", "Value cannot be negative."); 
      } 

      return result.ToString(); 
     } 
+1

je ne voudrais pas Divisé. Je boucle sur la chaîne à la recherche du prochain mot break. arrête si la position de la coupure trouvée est après la longueur donnée. sinon ajoutez le mot avant le constructeur de chaîne.pour trouver le mot avant la pause trouvée, vous devrez stocker la position de la pause précédemment trouvée (ou zéro). logique? – akonsu

+1

Vous ne pouvez pas vous soucier de votre application, mais gardez à l'esprit que les fonctions intégrées 'Trim' vérifient réellement' char.IsWhiteSpace', pas seulement 'space'. – Marc

+0

@Marc - bonne note. Je remettais en question ma formulation en la tapant. –

Répondre

14

Je voudrais utiliser string.LastIndexOf - au moins si nous nous intéressons seulement aux espaces. Ensuite, il n'y a pas besoin de créer des chaînes intermédiaires ...

Comme encore non testé:

public static string SmartTrim(this string text, int length) 
{ 
    if (text == null) 
    { 
     throw new ArgumentNullException("text"); 
    } 
    if (length < 0) 
    { 
     throw new ArgumentOutOfRangeException(); 
    } 
    if (text.Length <= length) 
    { 
     return text; 
    } 
    int lastSpaceBeforeMax = text.LastIndexOf(' ', length); 
    if (lastSpaceBeforeMax == -1) 
    { 
     // Perhaps define a strategy here? Could return empty string, 
     // or the original 
     throw new ArgumentException("Unable to trim word"); 
    } 
    return text.Substring(0, lastSpaceBeforeMax);   
} 

Code d'essai:

public class Test 
{ 
    static void Main() 
    { 
     Console.WriteLine("'{0}'", "foo bar baz".SmartTrim(20)); 
     Console.WriteLine("'{0}'", "foo bar baz".SmartTrim(3)); 
     Console.WriteLine("'{0}'", "foo bar baz".SmartTrim(4)); 
     Console.WriteLine("'{0}'", "foo bar baz".SmartTrim(5)); 
     Console.WriteLine("'{0}'", "foo bar baz".SmartTrim(7)); 
    } 
} 

Résultats:

'foo bar baz' 
'foo' 
'foo' 
'foo' 
'foo bar' 
+0

Alors, comment refactorisez-vous si l'exigence est une rupture de mot, pas seulement un espace? Spécifiquement le plus commun (où un mot pourrait se casser, mais le personnage n'a pas d'espace blanc autour d'elle) est le trait d'union ... Juste curieux. – AllenG

+1

@AllenG: Si c'est toujours dans un petit ensemble, 'text.LastIndexOfAny (Delimiters)' serait la meilleure option. –

2

Que diriez-vous d'une solution basée sur Regex? Vous voudrez probablement en tester d'autres et faire quelques vérifications de limites; mais c'est ce qui me vient à l'esprit:

using System; 
using System.Text.RegularExpressions; 

namespace Stackoverflow.Test 
{ 
    static class Test 
    { 
     private static readonly Regex regWords = new Regex("\\w+", RegexOptions.Compiled); 

     static void Main() 
     { 
      Console.WriteLine("The quick brown fox jumped over the lazy dog".SmartTrim(8)); 
      Console.WriteLine("The quick brown fox jumped over the lazy dog".SmartTrim(20)); 
      Console.WriteLine("Hello, I am attempting to build a string extension method to trim a string to a certain length but with not breaking a word. I wanted to check to see if there was anything built into the framework or a more clever method than mine".SmartTrim(100)); 
     } 

     public static string SmartTrim(this string s, int length) 
     { 
      var matches = regWords.Matches(s); 
      foreach (Match match in matches) 
      { 
       if (match.Index + match.Length > length) 
       { 
        int ln = match.Index + match.Length > s.Length ? s.Length : match.Index + match.Length; 
        return s.Substring(0, ln); 
       } 
      } 
      return s; 
     } 
    } 
} 
2

Essayez-le. Il est nul, ne se casse pas si la longueur est plus longue que la chaîne et implique moins de manipulation de chaîne.

Édition: Par recommandations, j'ai supprimé la chaîne intermédiaire. Je vais laisser la réponse telle qu'elle pourrait être utile dans les cas où des exceptions ne sont pas souhaitées.

public static string SmartTrim(this string s, int length) 
{ 
    if(s == null || length < 0 || s.Length <= length) 
     return s; 

    // Edit a' la Jon Skeet. Removes unnecessary intermediate string. Thanks! 
    // string temp = s.Length > length + 1 ? s.Remove(length+1) : s; 
    int lastSpace = s.LastIndexOf(' ', length + 1); 
    return lastSpace < 0 ? string.Empty : s.Remove(lastSpace); 
} 
+0

Pas mal, mais crée quand même une chaîne intermédiaire dans certains cas :) –

+0

Je pense que vous pouvez faites-le aussi: 's.LastIndexOf ('', length);' Et vous n'avez pas à faire votre ligne 'chaîne temp = ...'. – mlsteeves

+0

@mlsteeves: D'accord. @ La solution de Jon gère mieux 'LastIndexOf'. Je n'avais pas connaissance de l'autre dérogation. – kbrimington

1
string strTemp = "How are you doing today"; 
int nLength = 12; 
strTemp = strTemp.Substring(0, strTemp.Substring(0, nLength).LastIndexOf(' ')); 

I pense que ça devrait le faire. Quand j'ai couru ça, ça s'est terminé par "Comment vas-tu".

Ainsi, votre fonction serait:

public static string SmartTrim(this string s, int length) 
{ 
    return s.Substring(0, s.Substring(0, length).LastIndexOf(' '));; 
} 

Je voudrais certainement ajouter une gestion des exceptions si, comme en veillant à la longueur entière est pas supérieure à la longueur de la chaîne et non inférieure à 0.

+1

Cela échouera dans divers cas, par ex. si la longueur est plus longue que nécessaire, ou si un mot a exactement la bonne longueur, ou ne peut pas être coupé avec succès. –

+0

Oui, vous avez mis ce commentaire comme je faisais l'édition. :) Je pensais que je laisserais la gestion des exceptions à lui. – XstreamINsanity

1

obligatoire LINQ une doublure, si vous ne vous préoccupez espace comme limite de mot:

return new String(s.TakeWhile((ch,idx) => (idx < length) || (idx >= length && !Char.IsWhiteSpace(ch))).ToArray()); 
0

Je vais jeter dans une certaine bonté Linq même si d'autres ont répondu à cette ADEQ uately:

public string TrimString(string s, int maxLength) 
{ 
    var pos = s.Select((c, idx) => new { Char = c, Pos = idx }) 
     .Where(item => char.IsWhiteSpace(item.Char) && item.Pos <= maxLength) 
     .Select(item => item.Pos) 
     .SingleOrDefault(); 

    return pos > 0 ? s.Substring(0, pos) : s; 
} 

J'ai quitté le paramètre de vérifier que d'autres ont simplement pour accentuer le code ... important de

1

Utilisez comme cette

var substring = source.GetSubstring(50, new string[] { " ", "." })

Cette méthode peut obtenir un sous- chaîne basée sur un ou plusieurs caractères séparateurs

public static string GetSubstring(this string source, int length, params string[] options) 
    { 
     if (string.IsNullOrWhiteSpace(source)) 
     { 
      return string.Empty; 
     } 

     if (source.Length <= length) 
     { 
      return source; 
     } 

     var indices = 
      options.Select(
       separator => source.IndexOf(separator, length, StringComparison.CurrentCultureIgnoreCase)) 
       .Where(index => index >= 0) 
       .ToList(); 

     if (indices.Count > 0) 
     { 
      return source.Substring(0, indices.Min()); 
     } 

     return source; 
    } 
Questions connexes