.net-3.5 괄호 - C#에서 명명 된 문자열 서식 지정




regex (16)

여기 내가 잠시 뒤로 만들었습니다. 단일의 인수를 취하는 Format 메소드를 사용해 String를 확장합니다. 좋은 점은 표준 문자열을 사용할 것이라는 것입니다. int와 같은 간단한 인수를 제공하는 경우 형식을 지정하지만 익명 형식과 같은 것을 사용하면 작동합니다.

사용 예 :

"The {Name} family has {Children} children".Format(new { Children = 4, Name = "Smith" })

"스미스 가족은 4 명의 자녀가 있습니다."

그것은 배열과 인덱서 같은 미친 묶는 물건을하지 않습니다. 그러나 그것은 매우 간단하고 고성능입니다.

    public static class AdvancedFormatString
{

    /// <summary>
    /// An advanced version of string.Format.  If you pass a primitive object (string, int, etc), it acts like the regular string.Format.  If you pass an anonmymous type, you can name the paramters by property name.
    /// </summary>
    /// <param name="formatString"></param>
    /// <param name="arg"></param>
    /// <returns></returns>
    /// <example>
    /// "The {Name} family has {Children} children".Format(new { Children = 4, Name = "Smith" })
    /// 
    /// results in 
    /// "This Smith family has 4 children
    /// </example>
    public static string Format(this string formatString, object arg, IFormatProvider format = null)
    {
        if (arg == null)
            return formatString;

        var type = arg.GetType();
        if (Type.GetTypeCode(type) != TypeCode.Object || type.IsPrimitive)
            return string.Format(format, formatString, arg);

        var properties = TypeDescriptor.GetProperties(arg);
        return formatString.Format((property) =>
            {
                var value = properties[property].GetValue(arg);
                return Convert.ToString(value, format);
            });
    }


    public static string Format(this string formatString, Func<string, string> formatFragmentHandler)
    {
        if (string.IsNullOrEmpty(formatString))
            return formatString;
        Fragment[] fragments = GetParsedFragments(formatString);
        if (fragments == null || fragments.Length == 0)
            return formatString;

        return string.Join(string.Empty, fragments.Select(fragment =>
            {
                if (fragment.Type == FragmentType.Literal)
                    return fragment.Value;
                else
                    return formatFragmentHandler(fragment.Value);
            }).ToArray());
    }


    private static Fragment[] GetParsedFragments(string formatString)
    {
        Fragment[] fragments;
        if ( parsedStrings.TryGetValue(formatString, out fragments) )
        {
            return fragments;
        }
        lock (parsedStringsLock)
        {
            if ( !parsedStrings.TryGetValue(formatString, out fragments) )
            {
                fragments = Parse(formatString);
                parsedStrings.Add(formatString, fragments);
            }
        }
        return fragments;
    }

    private static Object parsedStringsLock = new Object();
    private static Dictionary<string,Fragment[]> parsedStrings = new Dictionary<string,Fragment[]>(StringComparer.Ordinal);

    const char OpeningDelimiter = '{';
    const char ClosingDelimiter = '}';

    /// <summary>
    /// Parses the given format string into a list of fragments.
    /// </summary>
    /// <param name="format"></param>
    /// <returns></returns>
    static Fragment[] Parse(string format)
    {
        int lastCharIndex = format.Length - 1;
        int currFragEndIndex;
        Fragment currFrag = ParseFragment(format, 0, out currFragEndIndex);

        if (currFragEndIndex == lastCharIndex)
        {
            return new Fragment[] { currFrag };
        }

        List<Fragment> fragments = new List<Fragment>();
        while (true)
        {
            fragments.Add(currFrag);
            if (currFragEndIndex == lastCharIndex)
            {
                break;
            }
            currFrag = ParseFragment(format, currFragEndIndex + 1, out currFragEndIndex);
        }
        return fragments.ToArray();

    }

    /// <summary>
    /// Finds the next delimiter from the starting index.
    /// </summary>
    static Fragment ParseFragment(string format, int startIndex, out int fragmentEndIndex)
    {
        bool foundEscapedDelimiter = false;
        FragmentType type = FragmentType.Literal;

        int numChars = format.Length;
        for (int i = startIndex; i < numChars; i++)
        {
            char currChar = format[i];
            bool isOpenBrace = currChar == OpeningDelimiter;
            bool isCloseBrace = isOpenBrace ? false : currChar == ClosingDelimiter;

            if (!isOpenBrace && !isCloseBrace)
            {
                continue;
            }
            else if (i < (numChars - 1) && format[i + 1] == currChar)
            {//{{ or }}
                i++;
                foundEscapedDelimiter = true;
            }
            else if (isOpenBrace)
            {
                if (i == startIndex)
                {
                    type = FragmentType.FormatItem;
                }
                else
                {

                    if (type == FragmentType.FormatItem)
                        throw new FormatException("Two consequtive unescaped { format item openers were found.  Either close the first or escape any literals with another {.");

                    //curr character is the opening of a new format item.  so we close this literal out
                    string literal = format.Substring(startIndex, i - startIndex);
                    if (foundEscapedDelimiter)
                        literal = ReplaceEscapes(literal);

                    fragmentEndIndex = i - 1;
                    return new Fragment(FragmentType.Literal, literal);
                }
            }
            else
            {//close bracket
                if (i == startIndex || type == FragmentType.Literal)
                    throw new FormatException("A } closing brace existed without an opening { brace.");

                string formatItem = format.Substring(startIndex + 1, i - startIndex - 1);
                if (foundEscapedDelimiter)
                    formatItem = ReplaceEscapes(formatItem);//a format item with a { or } in its name is crazy but it could be done
                fragmentEndIndex = i;
                return new Fragment(FragmentType.FormatItem, formatItem);
            }
        }

        if (type == FragmentType.FormatItem)
            throw new FormatException("A format item was opened with { but was never closed.");

        fragmentEndIndex = numChars - 1;
        string literalValue = format.Substring(startIndex);
        if (foundEscapedDelimiter)
            literalValue = ReplaceEscapes(literalValue);

        return new Fragment(FragmentType.Literal, literalValue);

    }

    /// <summary>
    /// Replaces escaped brackets, turning '{{' and '}}' into '{' and '}', respectively.
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    static string ReplaceEscapes(string value)
    {
        return value.Replace("{{", "{").Replace("}}", "}");
    }

    private enum FragmentType
    {
        Literal,
        FormatItem
    }

    private class Fragment
    {

        public Fragment(FragmentType type, string value)
        {
            Type = type;
            Value = value;
        }

        public FragmentType Type
        {
            get;
            private set;
        }

        /// <summary>
        /// The literal value, or the name of the fragment, depending on fragment type.
        /// </summary>
        public string Value
        {
            get;
            private set;
        }


    }

}

C #에서 위치를 지정하는 대신 이름으로 문자열을 포맷하는 방법이 있습니까?

파이썬에서는이 예제와 같은 것을 할 수 있습니다 ( here 에서 뻔뻔스럽게 도난당했습니다) :

>>> print '%(language)s has %(#)03d quote types.' % \
      {'language': "Python", "#": 2}
Python has 002 quote types.

C #에서 이것을 수행 할 수있는 방법이 있습니까? 예를 들면 다음과 같습니다.

String.Format("{some_variable}: {some_other_variable}", ...);

변수 이름을 사용하여이 작업을 수행 할 수 있다는 것은 좋지만 사전도 허용됩니다.


제 생각에 가장 가까운 것은 색인 된 형식입니다.

String.Format("{0} has {1} quote types.", "C#", "1");

String.Replace ()도 있습니다. 여러 단계로 해보고 문자열의 다른 곳에서 변수를 찾을 수 없다는 믿음으로 생각한다면 :

string MyString = "{language} has {n} quote types.";
MyString = MyString.Replace("{language}", "C#").Replace("{n}", "1");

목록을 사용하도록 확장 :

List<KeyValuePair<string, string>> replacements = GetFormatDictionary();  
foreach (KeyValuePair<string, string> item in replacements)
{
    MyString = MyString.Replace(item.Key, item.Value);
}

Dictionary <string, string>을 사용하여 .Keys 컬렉션을 iterating 할 수도 있지만 List <KeyValuePair <string, string >>을 사용하면 List의 .ForEach () 메소드를 활용하고 다시 요약 할 수 있습니다. 한 - 라이너 :

replacements.ForEach(delegate(KeyValuePair<string,string>) item) { MyString = MyString.Replace(item.Key, item.Value);});

람다는 더 간단 할 것이지만 여전히 .Net 2.0에 있습니다. 또한 .Netplace의 문자열은 불변이므로 반복적으로 사용하면 .Replace () 성능이 우수하지 않습니다. 또한 MyString 변수는 대리자가 액세스 할 수 있도록 정의해야하므로 완벽하지는 않습니다.


이것을 확인하십시오 :

public static string StringFormat(string format, object source)
{
    var matches = Regex.Matches(format, @"\{(.+?)\}");
    List<string> keys = (from Match matche in matches select matche.Groups[1].Value).ToList();

    return keys.Aggregate(
        format,
        (current, key) =>
        {
            int colonIndex = key.IndexOf(':');
            return current.Replace(
                "{" + key + "}",
                colonIndex > 0
                    ? DataBinder.Eval(source, key.Substring(0, colonIndex), "{0:" + key.Substring(colonIndex + 1) + "}")
                    : DataBinder.Eval(source, key).ToString());
        });
}

견본:

string format = "{foo} is a {bar} is a {baz} is a {qux:#.#} is a really big {fizzle}";
var o = new { foo = 123, bar = true, baz = "this is a test", qux = 123.45, fizzle = DateTime.Now };
Console.WriteLine(StringFormat(format, o));

성능은 다른 솔루션에 비해 꽤 좋습니다.


이것을 구현 한 것은 클래스를 사용할 때를 제외하고 String.Format의 기능을 복제하는 간단한 클래스입니다. 필드를 정의하기 위해 사전이나 유형을 사용할 수 있습니다.

https://github.com/SergueiFedorov/NamedFormatString

C # 6.0에서는이 기능을 언어 사양에 바로 추가하므로 NamedFormatString 은 이전 버전과의 호환성을위한 것입니다.


나는 이것이 가능할 지 의심 스럽다. 제일 먼저 염두에 두어야 할 점은 어떻게 지역 변수 이름에 액세스 할 것인가?

LINQ와 Lambda 표현식을 사용하여 영리한 방법으로이를 수행 할 수도 있습니다.


다음은 모든 객체에 대한 간단한 메소드입니다.

    using System.Text.RegularExpressions;
    using System.ComponentModel;

    public static string StringWithFormat(string format, object args)
    {
        Regex r = new Regex(@"\{([A-Za-z0-9_]+)\}");

        MatchCollection m = r.Matches(format);

        var properties = TypeDescriptor.GetProperties(args);

        foreach (Match item in m)
        {
            try
            {
                string propertyName = item.Groups[1].Value;
                format = format.Replace(item.Value, properties[propertyName].GetValue(args).ToString());
            }
            catch
            {
                throw new FormatException("The format string is not valid");
            }
        }

        return format;
    }

그리고 그것을 사용하는 방법은 다음과 같습니다.

 DateTime date = DateTime.Now;
 string dateString = StringWithFormat("{Month}/{Day}/{Year}", date);

출력 : 2/27/2012


필자는 기존 솔루션과 약간 다른 방식으로이 문제를 해결했습니다. 명명 된 항목 대체 (일부는 수행 한 반사 비트가 아님)의 핵심입니다. 그것은 매우 빠르고 간단합니다 ... 이것은 나의 해결책입니다 :

/// <summary>
/// Formats a string with named format items given a template dictionary of the items values to use.
/// </summary>
public class StringTemplateFormatter
{
    private readonly IFormatProvider _formatProvider;

    /// <summary>
    /// Constructs the formatter with the specified <see cref="IFormatProvider"/>.
    /// This is defaulted to <see cref="CultureInfo.CurrentCulture">CultureInfo.CurrentCulture</see> if none is provided.
    /// </summary>
    /// <param name="formatProvider"></param>
    public StringTemplateFormatter(IFormatProvider formatProvider = null)
    {
        _formatProvider = formatProvider ?? CultureInfo.CurrentCulture;
    }

    /// <summary>
    /// Formats a string with named format items given a template dictionary of the items values to use.
    /// </summary>
    /// <param name="text">The text template</param>
    /// <param name="templateValues">The named values to use as replacements in the formatted string.</param>
    /// <returns>The resultant text string with the template values replaced.</returns>
    public string FormatTemplate(string text, Dictionary<string, object> templateValues)
    {
        var formattableString = text;
        var values = new List<object>();
        foreach (KeyValuePair<string, object> value in templateValues)
        {
            var index = values.Count;
            formattableString = ReplaceFormattableItem(formattableString, value.Key, index);
            values.Add(value.Value);
        }
        return String.Format(_formatProvider, formattableString, values.ToArray());
    }

    /// <summary>
    /// Convert named string template item to numbered string template item that can be accepted by <see cref="string.Format(string,object[])">String.Format</see>
    /// </summary>
    /// <param name="formattableString">The string containing the named format item</param>
    /// <param name="itemName">The name of the format item</param>
    /// <param name="index">The index to use for the item value</param>
    /// <returns>The formattable string with the named item substituted with the numbered format item.</returns>
    private static string ReplaceFormattableItem(string formattableString, string itemName, int index)
    {
        return formattableString
            .Replace("{" + itemName + "}", "{" + index + "}")
            .Replace("{" + itemName + ",", "{" + index + ",")
            .Replace("{" + itemName + ":", "{" + index + ":");
    }
}

다음과 같은 방법으로 사용됩니다.

    [Test]
    public void FormatTemplate_GivenANamedGuid_FormattedWithB_ShouldFormatCorrectly()
    {
        // Arrange
        var template = "My guid {MyGuid:B} is awesome!";
        var templateValues = new Dictionary<string, object> { { "MyGuid", new Guid("{A4D2A7F1-421C-4A1D-9CB2-9C2E70B05E19}") } };
        var sut = new StringTemplateFormatter();
        // Act
        var result = sut.FormatTemplate(template, templateValues);
        //Assert
        Assert.That(result, Is.EqualTo("My guid {a4d2a7f1-421c-4a1d-9cb2-9c2e70b05e19} is awesome!"));
    }

희망이 누군가가 유용하다고!


비록 받아 들여진 대답이 좋은 예를 보여줍니다. Haack 예제와 .Inject는 이스케이프 처리를하지 않습니다. 또한 많은 사람들은 Regex (느린) 또는 .NET Core 및 일부 다른 환경에서는 사용할 수없는 DataBinder.Eval에 크게 의존합니다.

이를 염두에두고 필자는 문자를 통해 스트리밍하고 문자별로 StringBuilder 출력에 쓰는 간단한 상태 기반 파서를 작성했습니다. 이것은 String 확장 메소드로 구현되며 Dictionary<string, object> 또는 매개 변수가있는 object 를 입력으로 사용할 수 있습니다 (리플렉션 사용).

그것은 무제한 레벨의 {{{escaping}}} 하고 입력에 불균형 중괄호 및 / 또는 다른 오류가있을 때 FormatException throw합니다.

public static class StringExtension {
    /// <summary>
    /// Extension method that replaces keys in a string with the values of matching object properties.
    /// </summary>
    /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
    /// <param name="injectionObject">The object whose properties should be injected in the string</param>
    /// <returns>A version of the formatString string with keys replaced by (formatted) key values.</returns>
    public static string FormatWith(this string formatString, object injectionObject) {
        return formatString.FormatWith(GetPropertiesDictionary(injectionObject));
    }

    /// <summary>
    /// Extension method that replaces keys in a string with the values of matching dictionary entries.
    /// </summary>
    /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
    /// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
    /// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
    public static string FormatWith(this string formatString, IDictionary<string, object> dictionary) {
        char openBraceChar = '{';
        char closeBraceChar = '}';

        return FormatWith(formatString, dictionary, openBraceChar, closeBraceChar);
    }
        /// <summary>
        /// Extension method that replaces keys in a string with the values of matching dictionary entries.
        /// </summary>
        /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
        /// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
        /// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
    public static string FormatWith(this string formatString, IDictionary<string, object> dictionary, char openBraceChar, char closeBraceChar) {
        string result = formatString;
        if (dictionary == null || formatString == null)
            return result;

        // start the state machine!

        // ballpark output string as two times the length of the input string for performance (avoids reallocating the buffer as often).
        StringBuilder outputString = new StringBuilder(formatString.Length * 2);
        StringBuilder currentKey = new StringBuilder();

        bool insideBraces = false;

        int index = 0;
        while (index < formatString.Length) {
            if (!insideBraces) {
                // currently not inside a pair of braces in the format string
                if (formatString[index] == openBraceChar) {
                    // check if the brace is escaped
                    if (index < formatString.Length - 1 && formatString[index + 1] == openBraceChar) {
                        // add a brace to the output string
                        outputString.Append(openBraceChar);
                        // skip over braces
                        index += 2;
                        continue;
                    }
                    else {
                        // not an escaped brace, set state to inside brace
                        insideBraces = true;
                        index++;
                        continue;
                    }
                }
                else if (formatString[index] == closeBraceChar) {
                    // handle case where closing brace is encountered outside braces
                    if (index < formatString.Length - 1 && formatString[index + 1] == closeBraceChar) {
                        // this is an escaped closing brace, this is okay
                        // add a closing brace to the output string
                        outputString.Append(closeBraceChar);
                        // skip over braces
                        index += 2;
                        continue;
                    }
                    else {
                        // this is an unescaped closing brace outside of braces.
                        // throw a format exception
                        throw new FormatException($"Unmatched closing brace at position {index}");
                    }
                }
                else {
                    // the character has no special meaning, add it to the output string
                    outputString.Append(formatString[index]);
                    // move onto next character
                    index++;
                    continue;
                }
            }
            else {
                // currently inside a pair of braces in the format string
                // found an opening brace
                if (formatString[index] == openBraceChar) {
                    // check if the brace is escaped
                    if (index < formatString.Length - 1 && formatString[index + 1] == openBraceChar) {
                        // there are escaped braces within the key
                        // this is illegal, throw a format exception
                        throw new FormatException($"Illegal escaped opening braces within a parameter - index: {index}");
                    }
                    else {
                        // not an escaped brace, we have an unexpected opening brace within a pair of braces
                        throw new FormatException($"Unexpected opening brace inside a parameter - index: {index}");
                    }
                }
                else if (formatString[index] == closeBraceChar) {
                    // handle case where closing brace is encountered inside braces
                    // don't attempt to check for escaped braces here - always assume the first brace closes the braces
                    // since we cannot have escaped braces within parameters.

                    // set the state to be outside of any braces
                    insideBraces = false;

                    // jump over brace
                    index++;

                    // at this stage, a key is stored in current key that represents the text between the two braces
                    // do a lookup on this key
                    string key = currentKey.ToString();
                    // clear the stringbuilder for the key
                    currentKey.Clear();

                    object outObject;

                    if (!dictionary.TryGetValue(key, out outObject)) {
                        // the key was not found as a possible replacement, throw exception
                        throw new FormatException($"The parameter \"{key}\" was not present in the lookup dictionary");
                    }

                    // we now have the replacement value, add the value to the output string
                    outputString.Append(outObject);

                    // jump to next state
                    continue;
                } // if }
                else {
                    // character has no special meaning, add it to the current key
                    currentKey.Append(formatString[index]);
                    // move onto next character
                    index++;
                    continue;
                } // else
            } // if inside brace
        } // while

        // after the loop, if all braces were balanced, we should be outside all braces
        // if we're not, the input string was misformatted.
        if (insideBraces) {
            throw new FormatException("The format string ended before the parameter was closed.");
        }

        return outputString.ToString();
    }

    /// <summary>
    /// Creates a Dictionary from an objects properties, with the Key being the property's
    /// name and the Value being the properties value (of type object)
    /// </summary>
    /// <param name="properties">An object who's properties will be used</param>
    /// <returns>A <see cref="Dictionary"/> of property values </returns>
    private static Dictionary<string, object> GetPropertiesDictionary(object properties) {
        Dictionary<string, object> values = null;
        if (properties != null) {
            values = new Dictionary<string, object>();
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(properties);
            foreach (PropertyDescriptor prop in props) {
                values.Add(prop.Name, prop.GetValue(properties));
            }
        }
        return values;
    }
}

궁극적으로 모든 논리는 10 개의 주요 상태로 요약됩니다. - 상태 시스템이 대괄호 밖에 있고 대괄호 안에있는 경우에도 다음 문자는 대괄호, 이스케이프 된 대괄호, 닫힌 대괄호, 이스케이프 처리 된 닫힌 대괄호 또는 이스케이프 처리 된 대괄호입니다. 또는 일반 문자. 이러한 각각의 조건은 루프가 진행되면서 개별적으로 처리되어 출력 StringBuffer 또는 키 StringBuffer 문자를 추가합니다. 매개 변수가 닫히면 키 StringBuffer 의 값은 사전의 매개 변수 값을 조회하는 데 사용되며, 그런 다음 출력 StringBuffer 로 푸시됩니다. 마지막에는 출력 StringBuffer 의 값이 반환됩니다.


보간 된 문자열 이 C # 6.0 및 Visual Basic 14에 추가되었습니다.

두 가지 모두 Visual Studio 2015의 Roslyn 컴파일러를 통해 도입되었습니다.

  • C # 6.0 :

    return "\{someVariable} and also \{someOtherVariable}" 또는
    return $"{someVariable} and also {someOtherVariable}"

  • VB 14 :

    return $"{someVariable} and also {someOtherVariable}"

주목할만한 기능 (Visual Studio 2015 IDE에서) :

  • 구문 색상 지정 이 지원됩니다. 문자열에 포함 된 변수가 강조 표시됩니다.
  • 리팩토링 지원 - 이름을 바꿀 때 문자열에 포함 된 변수의 이름이 변경됨
  • 실제로는 변수 이름뿐 아니라 표현식 도 지원됩니다. 예를 들어 {index} 뿐만 아니라 {(index + 1).ToString().Trim()}

즐겨! (VS에서 "스마일 보내기"를 클릭하십시오)


private static Regex s_NamedFormatRegex = new Regex(@"\{(?!\{)(?<key>[\w]+)(:(?<fmt>(\{\{|\}\}|[^\{\}])*)?)?\}", RegexOptions.Compiled);

public static StringBuilder AppendNamedFormat(this StringBuilder builder,IFormatProvider provider, string format, IDictionary<string, object> args)
{
    if (builder == null) throw new ArgumentNullException("builder");
    var str = s_NamedFormatRegex.Replace(format, (mt) => {
        string key = mt.Groups["key"].Value;
        string fmt = mt.Groups["fmt"].Value;
        object value = null;
        if (args.TryGetValue(key,out value)) {
            return string.Format(provider, "{0:" + fmt + "}", value);
        } else {
            return mt.Value;
        }
    });
    builder.Append(str);
    return builder;
}

public static StringBuilder AppendNamedFormat(this StringBuilder builder, string format, IDictionary<string, object> args)
{
    if (builder == null) throw new ArgumentNullException("builder");
    return builder.AppendNamedFormat(null, format, args);
}

예:

var builder = new StringBuilder();
builder.AppendNamedFormat(
@"你好,{Name},今天是{Date:yyyy/MM/dd}, 这是你第{LoginTimes}次登录,积分{Score:{{ 0.00 }}}",
new Dictionary<string, object>() { 
    { "Name", "wayjet" },
    { "LoginTimes",18 },
    { "Score", 100.4 },
    { "Date",DateTime.Now }
});

출력 : 100.40, wayjet, 2011-05-04, 100. {100}


필자의 오픈 소스 라이브러리 인 Regextra 는 명명 된 형식 지정을 지원합니다 (다른 것들 중에서). 현재 .NET 4.0 이상을 대상으로하며 NuGet 사용할 수 있습니다. Regedxtra : 문제 해결에 도움 이되는 블로그 게시물을 가지고 있습니다. {2} .

명명 된 형식 지정 비트는 다음을 지원합니다.

  • 기본 서식
  • 중첩 된 속성 서식
  • 사전 형식 지정
  • 구분 기호 이스케이프
  • 표준 / 사용자 지정 / IFormatProvider 문자열 서식 지정

예:

var order = new
{
    Description = "Widget",
    OrderDate = DateTime.Now,
    Details = new
    {
        UnitPrice = 1500
    }
};

string template = "We just shipped your order of '{Description}', placed on {OrderDate:d}. Your {{credit}} card will be billed {Details.UnitPrice:C}.";

string result = Template.Format(template, order);
// or use the extension: template.FormatTemplate(order);

결과:

2014 년 2 월 28 일 '위젯'주문을 배송했습니다. 귀하의 {credit} 카드에 $ 1,500.00이 청구됩니다.

프로젝트의 GitHub 링크 (위)와 다른 예제를 보려면 wiki를 확인하십시오.


프레임 워크 자체는이를 수행하는 방법을 제공하지 않지만 Scott Hanselman 이이 게시물 을 살펴볼 수 있습니다. 사용 예 :

Person p = new Person();  
string foo = p.ToString("{Money:C} {LastName}, {ScottName} {BirthDate}");  
Assert.AreEqual("$3.43 Hanselman, {ScottName} 1/22/1974 12:00:00 AM", foo); 

James Newton-King 의이 코드 는 하위 속성 및 인덱스와 비슷하며 작동합니다.

string foo = "Top result for {Name} was {Results[0].Name}".FormatWith(student));

James의 코드는 System.Web.UI.DataBinder 를 사용하여 문자열을 구문 분석하고 System.Web 을 참조해야합니다. 일부 사용자는 비 웹 응용 프로그램에서 수행하기를 꺼립니다.

편집 : 오, 그들은 익숙한 형식으로 멋지게 작업 할 속성이 준비되어있는 개체가없는 경우 :

string name = ...;
DateTime date = ...;
string foo = "{Name} - {Birthday}".FormatWith(new { Name = name, Birthday = date });

string language = "Python";
int numquotes = 2;
string output = language + " has "+ numquotes + " language types.";

편집 : 내가 무슨 말을 했어야했다 "아니, 나는 당신이 뭘하기를 원하는지 믿을 수 없어 C #에 의해 지원됩니다. 이것은 당신이 얻을 것만 큼 가까이에있다."


상자에서 꺼내는 방법이없는 것처럼 보입니다. 그러나 값에 대한 IDictionary 에 링크하는 고유 한 IFormatProvider 를 구현하는 것이 가능할 것으로 보입니다.

var Stuff = new Dictionary<string, object> {
   { "language", "Python" },
   { "#", 2 }
};
var Formatter = new DictionaryFormatProvider();

// Interpret {0:x} where {0}=IDictionary and "x" is hash key
Console.WriteLine string.Format(Formatter, "{0:language} has {0:#} quote types", Stuff);

출력 :

Python has 2 quote types

주의해야 할 점은 FormatProviders 혼합 할 수 없기 때문에 멋진 텍스트 서식을 동시에 사용할 수 없다는 것입니다.



나는 Ritchers 책에서 lfousts 대답에 이것을 덧붙이고 싶습니다.

C # 언어 사양은 "스타일의 문제로 전체 시스템 유형 이름을 사용하는 것보다 키워드 사용이 유리합니다."라고 말하면서 "나는 언어 사양에 동의하지 않습니다. 필자는 FCL 유형 이름을 사용하고 원시 유형 이름을 완전히 피하는 것을 선호합니다. 사실, 컴파일러가 기본 유형 이름을 제공하지 않았기 때문에 개발자가 대신 FCL 유형 이름을 사용하기를 바랍니다. 내 이유는 다음과 같습니다.

나는 완전한 단락을 읽기 전에 그의 의견을 얻지 못했습니다.





c# .net-3.5 string-formatting