Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

String Truncation Function for C#

, 17 Dec 2009 Ms-PL
Rate this:
Please Sign up or sign in to vote.
There have been several times where I have needed to truncate strings for various reasons.  For example, on this TasteOfHome.com search results page, we needed to cut off the article body at a certain point, and add an ellipsis (…) to the end of the resulting string...

There have been several times where I have needed to truncate strings for various reasons.  For example, on this TasteOfHome.com search results page, we needed to cut off the article body at a certain point, and add an ellipsis (…) to the end of the resulting string.  The easy way to do this is to just take the first X characters and cut off the rest.  The problem is that this often leaves you with a word cut off in the middle.  So then you are left either finishing the current word or cutting off the string earlier, before the current word starts.  Also, sometimes a client wants to have the ellipsis at the end of the string, and sometimes they don't.  Rather than continually creating custom logic, or modifying the logic each time when someone changes his mind, I created a function that allows for all of these options.  I'm going to go over the function here in detail, so you can make use of it in your own work.  In addition, this should prove useful as a tutorial on using flags in C#.  If you don't have any knowledge in using flags in C#, check out this blog post on C# flags  I was going to go over flags in more detail, but when I found this, I felt that there was no need to rehash it, since this author goes over it perfectly.

To start off, here's the function in its entirety:

public static string TruncateString
	(string valueToTruncate, int maxLength, TruncateOptions options)
{
	if (valueToTruncate == null)
	{
		return "";
	}

	if (valueToTruncate.Length <= maxLength)
	{
		return valueToTruncate;
	}

	bool includeEllipsis = (options & TruncateOptions.IncludeEllipsis) == 
					TruncateOptions.IncludeEllipsis;
	bool finishWord = (options & TruncateOptions.FinishWord) == 
					TruncateOptions.FinishWord;
	bool allowLastWordOverflow = 
		(options & TruncateOptions.AllowLastWordToGoOverMaxLength) == 
		TruncateOptions.AllowLastWordToGoOverMaxLength;

	string retValue = valueToTruncate;

	if (includeEllipsis)
	{
		maxLength -= 1;
	}

	int lastSpaceIndex = retValue.LastIndexOf(" ", 
		maxLength, StringComparison.CurrentCultureIgnoreCase);

	if (!finishWord)
	{
		retValue = retValue.Remove(maxLength);
	}
	else if (allowLastWordOverflow)
	{
		int spaceIndex = retValue.IndexOf(" ", 
			maxLength, StringComparison.CurrentCultureIgnoreCase);
		if (spaceIndex != -1)
		{
			retValue = retValue.Remove(spaceIndex);
		}
	}
	else if (lastSpaceIndex > -1)
	{
		retValue = retValue.Remove(lastSpaceIndex);
	}

	if (includeEllipsis && retValue.Length < valueToTruncate.Length)
	{
		retValue += "&hellip;";
	}
	return retValue;
}

In addition, an enum is required to allow you to set the options for the truncation:

[Flags]
public enum TruncateOptions
{
	None = 0x0,
	FinishWord = 0x1,
	AllowLastWordToGoOverMaxLength = 0x2,
	IncludeEllipsis = 0x4
}

I have the TruncateString function as part of a "Tools" class (along with several other similar utility functions).  This class and the TruncateOptions enum are then put into a class library that can be included in projects as needed.  Then, to use the function, you just call it like this:

string originalString = "We the People of the United States, 
		in Order to form a more perfect Union, establish Justice, 
		insure domestic Tranquility, provide for the common defence, 
		promote the general Welfare, and secure the Blessings of Liberty 
		to ourselves and our Posterity, do ordain and establish this 
		Constitution for the United States of America.";

// This displays "We the People of the United States, in Order to fo"
Response.Write(Tools.TruncateString(originalString, 50, TruncateOptions.None));

// This displays "We the People of the United States, in Order to f…"
Response.Write(Tools.TruncateString(originalString, 50, TruncateOptions.IncludeEllipsis));

// This displays "We the People of the United States, in Order to…"
Response.Write(Tools.TruncateString(originalString, 50, 
	TruncateOptions.IncludeEllipsis | TruncateOptions.FinishWord));

// This displays "We the People of the United States, in Order to form…"
Response.Write(Tools.TruncateString(originalString, 50, 
	TruncateOptions.IncludeEllipsis | TruncateOptions.FinishWord | 
	TruncateOptions.AllowLastWordToGoOverMaxLength));

You may be asking, why all the work to use flags, when this function could have been done with a few booleans as parameters instead?  The reason for this is that the function can now be modified, adding additional options, without breaking the interface.  If I add another flag to TruncateOptions, I can modify TruncateString to make use of that new option and no existing functionality is broken, and I also don't have to go about making potentially dozens of function overrides.  Okay, dozens is probably an exaggeration in this situation, but in certain cases, it definitely could happen.

Function Break Down

Now, let's break down the function a bit to look into some specific logic:

if (valueToTruncate == null || maxLength <= 0)
{
	return "";
}

if (valueToTruncate.Length <= maxLength)
{
	return valueToTruncate;
}

Here, the function just handles a couple of default cases. No need to go through any of the logic if we know the resulting string will either be blank or the same as the original string.

if (includeEllipsis)
{
	maxLength -= 1;
}

This is optional. In my case, I want the ellipsis to count as one of the characters displayed, so a max length of 50 characters really is 50, not 51. If you don't want the ellipsis counted in the character count, you can just remove this code.

if (!finishWord)
{
	retValue = retValue.Remove(maxLength);
}

If the call doesn't require the word to be finished, then we can just cut off the string at the max length character.

else if (allowLastWordOverflow)
{
	int spaceIndex = retValue.IndexOf(" ", maxLength, 
		StringComparison.CurrentCultureIgnoreCase);
	if (spaceIndex != -1)
	{
		retValue = retValue.Remove(spaceIndex);
	}
}

Here it starts to get a little tricky. If the call allows the last word to go past the max character limit, then we need to finish the word started when we reach that character. To do this, we get the next space character after the max length. If we find a space, we trim the string up to that point. Otherwise, we leave it alone, because if there is no space after the max length, we are on the last word already.

else if (lastSpaceIndex > -1)
{
	retValue = retValue.Remove(lastSpaceIndex);
}

This case is when we don't want to allow the last word to go past the max length, but we want to finish a word. We check for a space within the max length string, and if we find one, we truncate the string at that point.

If none of these conditions get executed, we want to finish the current word, but there are no spaces to trim to, meaning that the string is a single word. In this situation, we just allow it to write out the full string; otherwise, we would be left with a blank string in this situation.

if (includeEllipsis && retValue.Length < valueToTruncate.Length)
{
	retValue += "&hellip;";
}

With this last bit of logic, we append the ellipsis if requested. The length check is for the situation mentioned just above, where the string we are trying to truncate is a single word and we want to finish the word; we don't want to include an ellipsis when we are displaying the whole string.


Posted to Blog and tagged as c, strings, programming

Similar Posts

  1. Graffiti CMS Plugin – Custom Categories for RSS Feed Items
  2. Getting Clients and Managing Expectations
  3. Social Media Dos and Don’ts

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Charles Boyung
Architect Nexus Technologies, LLC
United States United States
I have been working in the field of software development since 1999. With a degree in Computer Engineering from the Milwaukee School of Engineering, I try to provide a strong results-oriented approach to software development. I have worked with a variety of industries, including healthcare, magazine publishing and retail. After having worked for corporations of varying sizes for nearly ten years while also providing custom software solutions to individuals and small companies, I left the corporate world to provide expert, high-quality software solutions to a broader range of companies full-time. I am also a Certified Usability Analyst with Human Factors International, committed to providing the best possible experience to the users of your website or application.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberKarastgn24-Jan-13 20:54 
QuestionVery useful and well explained. Pinmemberlandreus31-Dec-12 4:43 
GeneralMy vote of 5 PinmemberDThrasher22-Nov-10 5:54 
GeneralThanks.... Pinmemberfadee16-Jan-10 8:42 
GeneralMy vote of 2 PinmemberTAFIN20-Dec-09 21:52 
GeneralRe: My vote of 2 PinmemberCharles Boyung12-Jan-10 4:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141223.1 | Last Updated 18 Dec 2009
Article Copyright 2009 by Charles Boyung
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid