聪明的方法在.Net中附加复数形式的's'(语法糖)

时间:2010-10-06 17:34:18

标签: c# .net string-formatting

我希望能够输入类似的内容:

Console.WriteLine("You have {0:life/lives} left.", player.Lives);

而不是

Console.WriteLine("You have {0} {1} left.", player.Lives, player.Lives == 1 ? "life" : "lives");

因此对于player.Lives == 1,输出将是:You have 1 life left.
对于player.Lives != 1You have 5 lives left.

Console.WriteLine("{0:day[s]} till doomsday.", tillDoomsdayTimeSpan);

有些系统内置了这种功能。我有多接近C#中的符号?

编辑:是的,我是专门寻找语法糖,而不是确定单数/复数形式的方法。

13 个答案:

答案 0 :(得分:73)

您可以签出属于.NET 4.0框架的PluralizationService类:

string lives = "life";
if (player.Lives != 1)
{
    lives = PluralizationService
        .CreateService(new CultureInfo("en-US"))
        .Pluralize(lives);
}
Console.WriteLine("You have {0} {1} left", player.Lives, lives);

值得注意的是,目前只支持英语。警告,这不适用于Net Framework 4.0 客户端配置文件

你也可以写一个扩展方法:

public static string Pluralize(this string value, int count)
{
    if (count == 1)
    {
        return value;
    }
    return PluralizationService
        .CreateService(new CultureInfo("en-US"))
        .Pluralize(value);
}

然后:

Console.WriteLine(
    "You have {0} {1} left", player.Lives, "life".Pluralize(player.Lives)
);

答案 1 :(得分:36)

您可以创建一个自定义格式化程序:

public class PluralFormatProvider : IFormatProvider, ICustomFormatter {

  public object GetFormat(Type formatType) {
    return this;
  }


  public string Format(string format, object arg, IFormatProvider formatProvider) {
    string[] forms = format.Split(';');
    int value = (int)arg;
    int form = value == 1 ? 0 : 1;
    return value.ToString() + " " + forms[form];
  }

}

Console.WriteLine方法没有带有自定义格式化程序的重载,因此您必须使用String.Format

Console.WriteLine(String.Format(
  new PluralFormatProvider(),
  "You have {0:life;lives} left, {1:apple;apples} and {2:eye;eyes}.",
  1, 0, 2)
);

输出:

You have 1 life left, 0 apples and 2 eyes.

注意:这是使格式化程序工作的最低要求,因此它不处理任何其他格式或数据类型。理想情况下,如果字符串中存在其他格式或数据类型,它将检测格式和数据类型,并将格式设置传递给默认格式化程序。

答案 2 :(得分:6)

使用@Darin Dimitrov解决方案,我会为string ....创建一个扩展名。

public static Extentions
{
    public static string Pluralize(this string str,int n)
    {
        if ( n != 1 )
            return PluralizationService.CreateService(new CultureInfo("en-US"))
            .Pluralize(str);
        return str;
    }
}

string.format("you have {0} {1} remaining",liveCount,"life".Pluralize());

答案 3 :(得分:5)

string message = string.format("You have {0} left.", player.Lives == 1 ? "life" : "lives");

当然,这假设你有多少个值可以复数。

答案 4 :(得分:5)

我写了一个名为 SmartFormat 的开源库,它正是这样做的!它是用C#编写的,并且在GitHub上: http://github.com/scottrippey/SmartFormat

虽然它支持多种语言,但英语“复数规则”是默认语言。这是语法:

var output = Smart.Format("You have {0} {0:life:lives} left.", player.Lives);

它还支持“零”数量和嵌套占位符,因此您可以执行以下操作:

var output = Smart.Format("You have {0:no lives:1 life:{0} lives} left.", player.Lives);

答案 5 :(得分:4)

查看属于Castle ActiveRecordInflector类。它是根据Apache许可证授权的。

它有一组正则表达式规则,用于定义单词的复数形式。我使用的版本在这些规则中有一些错误,例如,它有'病毒'→'病毒'规则。

我有三个包含Inflector的扩展方法,第一个可能就在你的街道上:

    /// <summary>
    /// Pluralises the singular form word specified.
    /// </summary>
    /// <param name="this">The singular form.</param>
    /// <param name="count">The count.</param>
    /// <returns>The word, pluralised if necessary.</returns>
    public static string Pluralise(this string @this, long count)
    {
        return (count == 1) ? @this :
                              Pluralise(@this);
    }

    /// <summary>
    /// Pluralises the singular form word specified.
    /// </summary>
    /// <param name="this">The singular form word.</param>
    /// <returns>The plural form.</returns>
    public static string Pluralise(this string @this)
    {
        return Inflector.Pluralize(@this);
    }

    /// <summary>
    /// Singularises the plural form word.
    /// </summary>
    /// <param name="this">The plural form word.</param>
    /// <returns>Th singular form.</returns>
    public static string Singularise(this string @this)
    {
        return Inflector.Singularize(@this);
    }

答案 6 :(得分:1)

我认为最简单的方法是创建一个接口IPlural,它有一个方法.ToString(int quantity),当quantity == 1复数形式返回单数形式时所有其他时间

答案 7 :(得分:1)

我与PluralizationService做了一些工作,然后提出来了。我刚刚为PluralizationService static提供了表现并将所有内容合并。

参考System.Data.Entity.Design

  using System.Data.Entity.Design.PluralizationServices;
  using System.Reflection;

  public static class Strings
  {
    private static PluralizationService pluralizationService = PluralizationService.CreateService(System.Globalization.CultureInfo.CurrentUICulture);
    public static string Pluralize(this MemberInfo memberInfo)//types, propertyinfos, ect
    {
      return Pluralize(memberInfo.Name.StripEnd());
    }

    public static string Pluralize(this string name)
    {
      return pluralizationService.Pluralize(name); // remove EF type suffix, if any
    }

    public static string StripEnd(this string name)
    {
      return name.Split('_')[0];
    }
  }

答案 8 :(得分:1)

对于C#6.0以后,你可以使用Interpolated Strings来做这个技巧。

示例:

    Console.WriteLine("\n --- For REGULAR NOUNS --- \n");
    {
        int count1 = 1;
        Console.WriteLine($"I have {count1} apple{(count1 == 1 ? "" : "s")}.");
        int count2 = 5;
        Console.WriteLine($"I have {count2} apple{(count2 == 1 ? "" : "s")}.");
    }

    Console.WriteLine("\n --- For IRREGULAR NOUNS --- \n");
    {
        int count1 = 1;
        Console.WriteLine($"He has {count1} {(count1 == 1 ? "leaf" : "leaves")}.");
        int count2 = 5;
        Console.WriteLine($"He has {count2} {(count2 == 1 ? "leaf" : "leaves")}.");
    }

输出:

 --- For REGULAR NOUNS --- 

I have 1 apple.
I have 5 apples.

 --- For IRREGULAR NOUNS --- 

He has 1 leaf.
He has 5 leaves.

你可以在.NET Fiddle上玩游戏。
有关详细信息,请转到Interpolated String documentation

答案 9 :(得分:1)

如果您的应用程序是英语,您可以使用此处显示的所有解决方案。但是,如果您计划本地化应用程序,则必须以正确的方式完成复数启用的消息。这意味着您可能需要多种模式(从1到6),具体取决于语言和规则,以选择使用模式的内容取决于语言。

例如,在英语中,您将有两种模式

“你有{0}活着” “你有{0}人的生命”

然后你将有一个Format函数,你可以使用liveAmount变量传递这两个模式。

Format("You have {0} live left", "You have {0} lives left", liveAmount);

在实际应用程序中,您不会对字符串进行硬编码,但会使用资源字符串。

格式会知道活动语言是什么,以及英语是否会使用

if (count == 1)
  useSingularPattern
else
  usePluralPattern

自己实现这个有点复杂,你不需要。你可以做一个我做过的开源项目

https://github.com/jaska45/I18N

通过使用它,您可以轻松获得字符串

var str = MultiPattern.Format("one;You have {0} live left;other;You have {0} lives left", liveAmount);

就是这样。库根据传递的liveAmount参数知道要使用的模式。规则已从CLDR提取到库.cs文件中。

如果要本地化应用程序,只需将多模式字符串放入.resx并让翻译器进行翻译即可。根据目标语言,多模式字符串可能包含1,2,3,4,5或6种模式。

答案 10 :(得分:0)

派对有点晚,但处理此问题的I wrote a library called MessageFormat.NET

var str = @"You have {lives, plural, 
                     zero {no lives} 
                      one {one life} 
                    other {# lives}
            } left.";
var result = MessageFormatter.Format(str, new {
    lives = 1337
});

文本周围的字符串中的空格不是必需的,只是为了便于阅读。

翻译时这很好,因为语言在多元化方面有不同的规则。

答案 11 :(得分:0)

查看通常编写的字符串如何同时考虑单个和多个值,例如

  

“预期{0}个文件,但找到了{1}个文件。”

我采用的方法是保持相同的字符串,但添加一些解析来确定是否应该完全删除(s),或者将s保留在圆括号内。对于不规则的单词,斜杠可以将单数和复数形式分开。

其他例子:

  
      
  • “有{0:n2}孩子(仁)”。Pluralize(1.0)=&gt; “有一个孩子的孩子”
  •   
  • “有{0}樱桃(ies)”。Pluralize(2)=&gt; “有2个樱桃”
  •   
  • “有{0}小牛/小牛”.Pluralize(1)=&gt; “有一头小牛”
  •   
  • “有{0}儿子在法律上”.Pluralize(2)=&gt; “有两个女儿”
  •   
  • “有{0}能海员/海员”.Pluralize(1)=&gt; “有一个能干的海员”
  •   
  • “有{0}只羊,{1}只山羊”。多次化(1,2)=&gt; “有一只羊,两只山羊”
  •   
///<summary>
/// Examples:
/// "{0} file(s)".Pluralize(1); -> "1 file"
/// "{0} file(s)".Pluralize(2); -> "2 files"
///</summary>
public static String Pluralize(this String s, params Object[] counts) {
    String[] arr = s.Split(new [] { ' ' }, StringSplitOptions.None);
    for (int i = 0; i < arr.Length; i++) {
        String t = arr[i];
        if (t.Length == 0 || t[0] != '{' || t[t.Length - 1] != '}')
            continue;

        int w = 1;
        while (w < t.Length) {
            char c = t[w];
            if (c < '0' || c > '9')
                break;
            w++;
        }

        if (w == 1)
            continue;

        int n = int.Parse(t.Substring(1, w-1));
        if (n >= counts.Length)
            continue;

        Object o = counts[n];
        if (o == null)
            continue;

        bool isSingle = false;
        if (o is int)
            isSingle = 1 == (int) o;
        else if (o is double)
            isSingle = 1 == (double) o;
        else if (o is float)
            isSingle = 1 == (float) o;
        else if (o is decimal)
            isSingle = 1 == (decimal) o;
        else if (o is byte)
            isSingle = 1 == (byte) o;
        else if (o is sbyte)
            isSingle = 1 == (sbyte) o;
        else if (o is short)
            isSingle = 1 == (short) o;
        else if (o is ushort)
            isSingle = 1 == (ushort) o;
        else if (o is uint)
            isSingle = 1 == (uint) o;
        else if (o is long)
            isSingle = 1 == (long) o;
        else if (o is ulong)
            isSingle = 1 == (ulong) o;
        else
            continue;

        for (int j = i + 1; j < arr.Length && j < i + 4; j++) {
            String u = arr[j];
            if (u.IndexOf('{') >= 0)
                break; // couldn't find plural word and ran into next token

            int b1 = u.IndexOf('(');
            int b2 = u.IndexOf(')', b1 + 1);
            if (b1 >= 0 && b2 >= 0) {
                String u1 = u.Substring(0, b1);
                String u2 = u.Substring(b2+1);
                char last = (u1.Length > 0 ? u1[u1.Length - 1] : ' ');
                String v = (isSingle ? "" : u.Substring(b1+1, (b2 - b1) - 1));
                if ((last == 'y' || last == 'Y') && String.Compare(v, "ies", true) == 0)
                    u1 = u1.TrimEnd('y', 'Y');

                arr[j] = u1 + v + u2;
                break;
            }
            int s1 = u.IndexOf('/');
            if (s1 >= 0) {
                arr[j] = (isSingle ? u.Substring(0, s1) : u.Substring(s1 + 1));
                break;
            }
        }
    }

    s = String.Join(" ", arr);
    s = String.Format(s, counts);
    return s;
}

答案 12 :(得分:0)

我在.NET 4.6中使用此扩展方法

public static string Pluralize(this string @string)
{
     if (string.IsNullOrEmpty(@string)) return string.Empty;

     var service = new EnglishPluralizationService();

     return service.Pluralize(@string);
}