Skip to content

Instantly share code, notes, and snippets.

@nikanos
Created January 3, 2023 20:19
Show Gist options
  • Save nikanos/b6db3f3d133eda2e741815d1174504f4 to your computer and use it in GitHub Desktop.
Save nikanos/b6db3f3d133eda2e741815d1174504f4 to your computer and use it in GitHub Desktop.
C# string SafeFormat method - used as a wrapper to string.Format() and passing Undefined to any missing arguments
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
public class StringUtils
{
public static string SafeFormat(string format, params object[] parameters)
{
const int MAX_NUMBER_OF_UNPASSED_PARAMETERS = 50;
const string UNDEFINED_PARAMETER_VALUE = "Undefined";
List<object> parametersList = new List<object>(parameters);
List<int> parameterIndexes = new List<int>();
// Use negative look behind to skip double {
// Capture the indexes digits after { in groups named "ParameterIndex"
const string RE_PATTERN = @"(?<!{){(?<ParameterIndex>\d+)";
Regex re = new Regex(RE_PATTERN);
MatchCollection matches = re.Matches(format);
foreach (Match match in matches)
parameterIndexes.Add(Convert.ToInt32(match.Groups["ParameterIndex"].Value));
if (parameterIndexes.Any())
{
int maxIndex = parameterIndexes.Max();
if (maxIndex >= parametersList.Count)
{
int parametersToAdd = (maxIndex - parametersList.Count) + 1;
if (parametersToAdd > MAX_NUMBER_OF_UNPASSED_PARAMETERS)
throw new FormatException($"Invalid format {format}.It would require {parametersToAdd} more arguments to pass");
parametersList.AddRange(Enumerable.Repeat(UNDEFINED_PARAMETER_VALUE, parametersToAdd));
}
}
return string.Format(format, parametersList.ToArray());
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
[TestClass]
public class StringUtilsTests
{
[TestMethod]
public void StringUtilsSafeFormat_FormatWithoutPlaceholders_Succeeds()
{
string template = "num";
string result = StringUtils.SafeFormat(template);
Assert.IsNotNull(result);
Assert.AreEqual("num", result);
}
[TestMethod]
public void StringUtilsSafeFormat_WithCorrectNumberOfArguments_Succeeds()
{
string template = "num {0}";
string result = StringUtils.SafeFormat(template, 1);
Assert.IsNotNull(result);
Assert.AreEqual("num 1", result);
}
[TestMethod]
public void StringUtilsSafeFormat_WithCorrectNumberOfArgumentsAndHexFormatting_Succeeds()
{
string template = "num {0:X}";
string result = StringUtils.SafeFormat(template, 255);
Assert.IsNotNull(result);
Assert.AreEqual("num FF", result);
}
[TestMethod]
public void StringUtilsSafeFormat_WithFewerNumberOfArguments_SucceedsAndFillsMissingArguments()
{
string template = "num {0}";
string result = StringUtils.SafeFormat(template);
Assert.IsNotNull(result);
Assert.AreEqual("num Undefined", result);
}
[TestMethod]
[ExpectedException(typeof(FormatException))]
public void StringUtilsSafeFormat_WithTemplateThatWouldRequireManyArgumentsToBeAdded_Throws()
{
string template = "num {100}";
string result = StringUtils.SafeFormat(template);
}
[TestMethod]
public void StringUtilsSafeFormat_WithTemplateThatHasDoubleSquareBrackets_SucceedsAndDoesNoReplacement()
{
string template = "num {{1}}";
string result = StringUtils.SafeFormat(template);
Assert.IsNotNull(result);
Assert.AreEqual("num {1}", result);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment