用户输入的字符串插值

String interpolation by user input

是否可以根据用户输入进行插值?

例如这样的事情:

public string Foo(string input)
{
    string user = "random_user_name";
    string address = "random_address";
    string city = "random_city";

    return $"{input}";
}
  • Example input1: "{user}, {city}"
  • Expected output1: "random_user_name, random_city"
  • Example input2: "{address}"
  • Expected output2: "random_address"
  • Example input3: "{city}, {address}"
  • Expected output3: "random_city, random_address"

我需要用户可以定义需要哪些变量及其顺序。顺便说一句,用户会知道变量名。

您可以简单地使用 string.Format

public string Foo(string input)
{
    string user = "random_user_name";
    string address = "random_address";
    string city = "random_city";

    return string.Format(input, user, address, city);
}

例子

Foo("{0}, {2}")  // "random_user_name, random_city"
Foo("{1}")       // "random_address"
Foo("{2}, {1}")  // "random_city, random_address"

dotnetfiddle

缺点是函数的用户需要知道您传递参数的顺序。

根据@TheGeneral 的建议,我做了以下工作:

我做的第一件事是将你的三个字符串更改为 Dictionary<string, string>

var tokens = new Dictionary<string, string> {
    { "user", "random_user_name" },
    { "address", "random_address" },
    { "city",  "random_city" }
};

然后我创建了一个辅助函数,按照@TheGeneral 描述的方式进行替换

public static string DoReplacement(string key, Dictionary<string, string> tokens)
{
    var result = new StringBuilder(key);
    foreach (var kvp in tokens)
    {
        result.Replace("{" + kvp.Key + "}", kvp.Value);
    }
    return result.ToString();
}

最后,我把它们打包在一起:

public static string Test(string input)
{
    var tokens = new Dictionary<string, string> {
        { "user", "random_user_name" },
        { "address", "random_address" },
        { "city",  "random_city" }
    };

    return $"{DoReplacement(input, tokens)}";
}

为了练习这个,我用 OP 的例子调用它:

var tests = new List<string> {
    "{user}, {city}",
    "{address}",
    "{city}, {address}",
};
foreach (var s in tests)
{
    var replaced = StringReplacement.Test(s);
    Debug.WriteLine(replaced);
}

得到这个输出:

random_user_name, random_city
random_address
random_city, random_address

基于 Charlieface 的建议.. 过去我接受了用户的输入并将其转换为格式兼容的输入字符串,因为 知道我将传递给格式的顺序..

我知道我在体重、姓名、生日订单中调用格式

我能做到:

public class Formatter{

    private string _fmt;

    public Formatter(string theirInput){
      _fmt = theirInput;

      ord = new[] { "{weight", "{name", "{birthday" };
      for(int x = 0; x < ord.Length; x++)
        _fmt = _fmt.Replace(ord[x], "{"+x);
    }

    public string Format(User u){
      return string.Format(_fmt, u.Weight, u.Name, u.Birthday);
    }
}

假设用户输入 {name} {weight} {birthday}

此代码将其转换为 {1} {0} {2},然后存储转换结果

何必呢?好吧,如果它只会发生一次,您还不如在构造函数中使用字符串替换模式来直接用值替换变量。这样做的好处是,如果你有多个东西要以相同的方式格式化,额外的好处,它允许用户指定可能因文化而异的格式

例如,您的用户可以传递 "Mr {name} born on {birthday:yyyy-MM-dd} weighing {weight:F2} lbs to two decimal places" 并且 string.Format 会调整其生日 DateTime 的格式以遵循 yyyy-MM-did 和双倍重量(根据公斤计算,可能有 15小数位)保留两位小数。

您可以在文档中阅读有关字符串格式和参数到占位符的更多信息:

https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings

https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings

https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings

https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings

https://docs.microsoft.com/en-us/dotnet/api/system.string.format?view=net-5.0

与我的其他答案完全不同的技术,它使用正则表达式匹配评估器来提供替换值

您的代码有一个替换字典:

var tokens = new Dictionary<string, string> {
    { "{user}", "random_user_name" },
    { "{address}", "random_address" },
    { "{city}",  "random_city" }
};

我们用键制作了一个正则表达式:

var r = new Regex(string.Join("|", tokens.Keys.Select(Regex.Escape)));

我们制作了一个匹配评估器,它基本上是一种在正则表达式找到某个匹配值时决定使用什么替换值的方法:

var me = new MatchEvaluator(m => tokens[m.Value]);

我们根据他们的输入调用它

var output = r.Replace(sInput, me);

从键构建了一个字符串,如 {user}|{address}|{city} Regex 将在输入字符串中以 OR 样式找到它们。每次找到一个,它都会询问匹配评估器,例如“找到 'user' 我应该用什么替换它?”并且匹配评估器 lambda 基本上在字典中查找“用户”并返回“random_user_name”

请注意,如果您在 Regex 上启用不区分大小写的匹配,您应该执行一些操作,例如将字典中的键小写并在匹配评估器查找中调用 ToLower,否则您可能会得到 keyNotFound