基于Dictionary的字符串格式化

有时候,我们需要一种基于名称的格式化方法,而不是基于位置的格式化,String.Format方法就是基于位置的格式化方法,使用方法如下:

string s = string.Format("{0} first, {1} second", 3.14, DateTime.Now);

 

基于位置的格式化方法在位置变动的时候,修改起来很麻烦。而且可读性也不好。所以我们可能需要如下格式化方法:

Dictionary<string, object> testDic = new Dictionary<string, object>

      { { "TestKey", "Helloworld" },

        { "foo" , 123.45 },

        { "bar" , 42},

        { "date", date}

      };

string result = StringFormatter.Format("TestKey is {TestKey}", testDic);

从某种意义上来说, 第二种方案显得更清晰。完整的实现代码和UnitTest代码如下:

public class StringFormatter

  {

    /// <summary>

    /// Replaces the format item in a specified String with the text equivalent of the value of a corresponding Object instance in a specified array.

    /// Using key name in the format string to refer a object in the args dictionary with the same key

    /// </summary>

    /// <param name="format">A composite format string. </param>

    /// <param name="args">A dictionary containing zero or more objects to format.</param>

    /// <returns>A copy of format in which the format items have been replaced by the String equivalent of the corresponding instances of Object in args.</returns>

    public static string Format(string format, Dictionary<string, object> args)

    {

      if ((format == null) || (args == null))

      {

        throw new ArgumentNullException((format == null) ? "format" : "args");

      }

 

      StringBuilder strBuilder = new StringBuilder(format.Length + args.Count * 8);

      StringBuilder expBuilder = new StringBuilder(20);

      StringBuilder keyBuilder = new StringBuilder(20);

 

      char[] chArray = format.ToCharArray();     

      bool hasFoundStartBrace = false;

      bool hasGotKeyName = false;

 

      for (int i = 0; i < chArray.Length; i++)

      {

        if (!hasFoundStartBrace)

        {

          if (chArray[i] == '{')

          {

            if (i + 1 == chArray.Length)

            {

              ThrowFormatException();

            }

            else if (chArray[i + 1] == '{')

            {

              strBuilder.Append(chArray[i]);

              i++;

            }

            else

            {

              hasFoundStartBrace = true;

              expBuilder.Append(chArray[i]);             

            }

          }

          else if(chArray[i] == '}')

          {

            if (i + 1 == chArray.Length)

            {

              ThrowFormatException();

            }

            else if (chArray[i + 1] == '}')

            {

              strBuilder.Append(chArray[i]);

              i++;

            }

            else

            {

              ThrowFormatException();

            }

          }

          else

          {

            strBuilder.Append(chArray[i]);

          }

        }

        else

        {         

          if (chArray[i] == '}')

          {

            if (!hasGotKeyName)

            {

              hasGotKeyName = true;

              expBuilder.Append('0');

            }

 

            expBuilder.Append('}');

           

            object value;

            string keyName = keyBuilder.ToString();

            if (!args.TryGetValue(keyName, out value))

            {

              throw new KeyNotFoundException(string.Format("Key '{0}' not found", keyName));

            }

           

            strBuilder.AppendFormat(expBuilder.ToString(), value);

            expBuilder.Remove(0, expBuilder.Length);

            keyBuilder.Remove(0, keyBuilder.Length);

            hasFoundStartBrace = false;

            hasGotKeyName = false;

          }

          else

          {

            if (hasGotKeyName)

            {

              expBuilder.Append(chArray[i]);

            }

            else

            {

              if (chArray[i] != ',' && chArray[i] != ':')

              {

                keyBuilder.Append(chArray[i]);

              }

              else

              {

                expBuilder.Append('0');

                expBuilder.Append(chArray[i]);

                hasGotKeyName = true;

              }

            }

          }

        }

      }

 

      if (hasFoundStartBrace)

      {

        ThrowFormatException();

      }

 

      return strBuilder.ToString();

    }

   

    private static void ThrowFormatException()

    {

      throw new FormatException("Input string was not in a correct format.");

    }

  }

 

/// <summary>

    ///This is a test class for StringFormatterTest and is intended

    ///to contain all StringFormatterTest Unit Tests

    ///</summary>

  [TestClass()]

  public class StringFormatterTest

  {

 

 

    private TestContext testContextInstance;

 

    /// <summary>

    ///Gets or sets the test context which provides

    ///information about and functionality for the current test run.

    ///</summary>

    public TestContext TestContext

    {

      get

      {

        return testContextInstance;

      }

      set

      {

        testContextInstance = value;

      }

    }

 

    #region Additional test attributes

    //

    //You can use the following additional attributes as you write your tests:

    //

    //Use ClassInitialize to run code before running the first test in the class

    [ClassInitialize()]

    public static void ClassInitialize(TestContext testContext)

    {

      date = DateTime.Now;

 

      testDic = new Dictionary<string, object>

      { { "TestKey", "Helloworld" },

        { "foo" , 123.45 },

        { "bar" , 42},

        { "date", date}

      };

    }

    //

    //Use ClassCleanup to run code after all tests in a class have run

    //[ClassCleanup()]

    //public static void MyClassCleanup()

    //{

    //}

    //

    //Use TestInitialize to run code before running each test

    //[TestInitialize()]

    //public void MyTestInitialize()

    //{

    //}

    //

    //Use TestCleanup to run code after each test has run

    //[TestCleanup()]

    //public void MyTestCleanup()

    //{

    //}

    //

    #endregion

 

 

    private static DateTime date;

    private static Dictionary<string, object> testDic;

 

    /// <summary>

    /// Format an empty string, the result should be empty and not throw an exception

    /// </summary>

    [TestMethod()]

    public void FormatEmptyString()

    {

      string result = StringFormatter.Format(string.Empty, testDic);

      Assert.AreEqual<string>(string.Empty, result);

    }

 

    /// <summary>

    /// Formatting a string contains no key should get the same string

    /// </summary>

    [TestMethod()]

    public void NoKeyReferToDictionary()

    {

      string result = StringFormatter.Format("TestKey", testDic);

      Assert.AreEqual<string>("TestKey", result);

    }

 

    /// <summary>

    /// Escape leading brace at the begining position in format string

    /// </summary>

    [TestMethod()]

    public void EscapeLeadingBraceAtBegining()

    {

      string result = StringFormatter.Format("{{TestKey", testDic);

      Assert.AreEqual<string>("{TestKey", result);

    }

 

    /// <summary>

    /// Escape leading brace at the end position in format string

    /// </summary>

    [TestMethod()]

    public void EscapeLeadingBraceAtEnd()

    {

      string result = StringFormatter.Format("TestKey{{", testDic);

      Assert.AreEqual<string>("TestKey{", result);

    }

 

    /// <summary>

    /// Escape trailing brace at the begining position in format string

    /// </summary>

    [TestMethod()]

    public void EscapeTrailingBraceAtBegining()

    {

      string result = StringFormatter.Format("}}TestKey", testDic);

      Assert.AreEqual<string>("}TestKey", result);

    }

 

    /// <summary>

    /// Escape trailing brace at the end position in format string

    /// </summary>

    [TestMethod()]

    public void EscapeTrailingBraceAtEnd()

    {

      string result = StringFormatter.Format("TestKey}}", testDic);

      Assert.AreEqual<string>("TestKey}", result);

    }

 

    /// <summary>

    /// Escape both leading brace and trailing brace in format string for one time

    /// </summary>

    [TestMethod()]

    public void EscapeLeadingBraceAndTrailingBraceOnce()

    {

      string result = StringFormatter.Format("{{TestKey}}", testDic);

      Assert.AreEqual<string>("{TestKey}", result);

    }

 

    /// <summary>

    /// Escape both leading brace and trailing brace in format string for two times

    /// </summary>

    [TestMethod()]

    public void EscapeLeadingBraceAndTrailingBraceTwice()

    {

      string result = StringFormatter.Format("{{{{TestKey}}}}", testDic);

      Assert.AreEqual<string>("{{TestKey}}", result);

    }

 

    /// <summary>

    /// Format string value referrd by a key contained in format string

    /// </summary>

    [TestMethod()]

    public void FormatStringValue()

    {

      string result = StringFormatter.Format("begin-{TestKey}-end", testDic);

      Assert.AreEqual<string>("begin-Helloworld-end", result);

    }

 

    /// <summary>

    ///  Format string value and Escape both leading brace and trailing brace once

    /// </summary>

    [TestMethod()]

    public void EscapeLeadingBraceAndTrailingBraceOnce_FormatStringValue()

    {

      string result = StringFormatter.Format("{{{TestKey}}}", testDic);

      Assert.AreEqual<string>("{Helloworld}", result);

    }

 

    /// <summary>

    /// Format string value and Escape both leading brace and trailing brace twice

    /// </summary>

    [TestMethod()]

    public void EscapeLeadingBraceAndTrailingBraceTwice_FormatStringValue()

    {

      string result = StringFormatter.Format("{{{{{TestKey}}}}}", testDic);

      Assert.AreEqual<string>("{{Helloworld}}", result);

    }

 

    /// <summary>

    /// Test formatted string's alignment

    /// </summary>

    [TestMethod()]

    public void FormatStringValue_TestAlignment()

    {

      string result = StringFormatter.Format("{TestKey,20}", testDic);

      Assert.AreEqual<string>("          Helloworld", result);

    }

 

    /// <summary>

    /// Format date value

    /// </summary>

    [TestMethod()]

    public void FormatDateValue()

    {

      string result = StringFormatter.Format("{date:yyyyMMdd}", testDic);

      Assert.AreEqual<string>(date.ToString("yyyyMMdd"), result);

    }

 

    /// <summary>

    /// Format date value and Test formatted string's alignment

    /// </summary>

    [TestMethod()]

    public void FormatDateValue_TestAlignment()

    {

      string result = StringFormatter.Format("{date,20:yyyyMMdd}", testDic);

      Assert.AreEqual<string>(new string(' ', 12) + date.ToString("yyyyMMdd"), result);

    }

 

    /// <summary>

    /// Format multiple values

    /// </summary>

    [TestMethod()]

    public void FormatMultipleValues()

    {

      string result = StringFormatter.Format("{foo} {foo} {bar}{TestKey}", testDic);

      Assert.AreEqual<string>("123.45 123.45 42Helloworld", result);

    }

 

    /// <summary>

    /// Format numeric value

    /// </summary>

    [TestMethod()]

    public void FormatNumericValue()

    {

      string result = StringFormatter.Format("{foo:#.#}", testDic);

      Assert.AreEqual<string>("123.5", result);

    }

 

    /// <summary>

    /// Test throwing FormatException

    /// </summary>

    [TestMethod]

    [ExpectedException(typeof(FormatException))]

    public void ExceptionTest_FormatException()

    {

      string result = StringFormatter.Format("{{T}", new Dictionary<string, object>());

    }

 

    /// <summary>

    /// Test throwing FormatException

    /// </summary>

    [TestMethod]

    [ExpectedException(typeof(FormatException))]

    public void ExceptionTest_OnlyOneLeadingBrace()

    {

      string result = StringFormatter.Format("{", new Dictionary<string, object>());

    }

 

    /// <summary>

    /// Test throwing FormatException

    /// </summary>

    [TestMethod]

    [ExpectedException(typeof(FormatException))]

    public void ExceptionTest_OnlyOneTrailingBrace()

    {

      string result = StringFormatter.Format("}", new Dictionary<string, object>());

    }

 

    /// <summary>

    /// Test throwing FormatException

    /// </summary>

    [TestMethod]

    [ExpectedException(typeof(ArgumentNullException))]

    public void ExceptionTest_ArugumentNull()

    {

      string result = StringFormatter.Format(null, null);

    }

 

    /// <summary>

    /// Test throwing KeyNotFoundException

    /// </summary>

    [TestMethod]

    [ExpectedException(typeof(KeyNotFoundException))]

    public void ExceptionTest_KeyNotFound()

    {

      string result = StringFormatter.Format("{inexistencekey:29}", new Dictionary<string, object>());

    }

  }

你可能感兴趣的:(Date,object,String,Dictionary,alignment,attributes)