Knowledge.ToString()

Parse Number List and Number Ranges in C#

Have you ever had a need to parse list of numbers and number ranges? Wanted to convert it into strongly typed data?

For example, string “2-5, 7, 9-15” should be converted into strongly typed objects for easier handling.

Input is a string which consists of comma separate numbers or number ranges. Number ranges are indicated using hyphen character. Output is a strongly typed number range. If the input is a single number, output will be a number range with same start and end number.

Parsing a number list and number range is really useful if you are dealing with document pages and you want to provide a way for user to select specific pages.

Here is an example to implement number range parser in C#.

public class NumberRange
{
	public int StartNumber { get; private set; }
	public int EndNumber { get; private set; }

	public NumberRange(int startNumber, int endNumber)
	{
		this.StartNumber = startNumber;
		this.EndNumber = endNumber;
	}
}

NumberRange is a POCO object. String data will be converted into this strongly typed object.

public class NumberRangeParserResult
{
	public bool IsParsedSuccessfully { get; private set; }
	public List<string> Errors { get; private set; }

	public List<NumberRange> ParsedRanges { get; private set; }

	public NumberRangeParserResult(bool isParsedSuccessfully, List<string> errors, List<NumberRange> parsedRanges)
	{
		this.IsParsedSuccessfully = isParsedSuccessfully;
		this.Errors = errors;
		this.ParsedRanges = parsedRanges;
	}
}

NumberRangeParserResult provides a unified way to get either the list of number ranges or list of errors.

public class NumberRangeParser
{
	public static NumberRangeParserResult Parse(string inputValue, int minNumber, int maxNumber)
	{
		string[] rangeSeparator = { "," };
		string[] rangeSplitter = { "-" };
		List<string> errors = new List<string>();
		List<NumberRange> parsedRanges = new List<NumberRange>();
		// split input string to get multiple ranges
		string[] ranges = inputValue.Split(rangeSeparator, StringSplitOptions.RemoveEmptyEntries);
		foreach (string range in ranges)
		{
			// try to split range to check if it is a range or individual number
			string trimmedRange = range.Trim();
			string[] rangeValues = trimmedRange.Split(rangeSplitter, StringSplitOptions.RemoveEmptyEntries);
			if (rangeValues.Length > 2)
			{
				errors.Add($"Range {trimmedRange} is not in a correct format. Correct format is NN-NN");
				continue;
			}
			int startNumber = 0, endNumber = 0;

			if (!int.TryParse(rangeValues[0].Trim(), out startNumber))
			{
				errors.Add($"Range {trimmedRange} does not contain valid number");
				continue;
			}
			if (rangeValues.Length == 2)
			{
				if (!int.TryParse(rangeValues[1].Trim(), out endNumber))
				{
					errors.Add($"Range {trimmedRange} does not contain valid number");
					continue;
				}
			}
			else
			{
				endNumber = startNumber;
			}

			if (startNumber > endNumber)
			{
				errors.Add($"Starting number is greater than ending number for range {trimmedRange}");
				continue;
			}

			if(startNumber < minNumber || endNumber > maxNumber)
			{
				errors.Add($"Range {trimmedRange} is invalid. Please provide numbers between {minNumber} and {maxNumber}.");
			}
			// add item to list
			parsedRanges.Add(new NumberRange(startNumber, endNumber));
		} // end foreach

		if (errors.Count > 0)
		{
			// clear out data to prep for result
			parsedRanges.Clear();
		}

		return new NumberRangeParserResult(errors.Count == 0, errors, parsedRanges);

	}

}

NumberRangeParser is the main class responsible for parsing string. You may add additional conditions or modify error messages based on your need.

Here is how you use the NumberRangeParser class.

int minNumber = 1;
int maxNumber = 20;
var result = NumberRangeParser.Parse("1-4, 7, 9, 12-17", minNumber, maxNumber);
if (!result.IsParsedSuccessfully)
{
	// show error message and return
	MessageBox.Show(string.Join(",", result.Errors));
	return;
}
else
{
	// use result.ParsedRanges as per your need
}

Share

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *