我可以在运行时扩展包含C#文字表达式的字符串

时间:2010-07-21 10:02:25

标签: c#

如果我有一个包含c#字符串文字表达式的字符串,我可以在运行时“展开”它

    public void TestEvaluateString()
    {
        string Dummy = EvalString( @"Contains \r\n new line");
        Debug.Assert(Dummy == "Contains \r\n new line");
    }

    private string EvalString(string input)
    {
        return "Contains \r\n new line";
    }

Can I convert a C# string value to an escaped string literal相似,但相反?

4 个答案:

答案 0 :(得分:10)

与Mikael的答案类似,但使用的是CSharpCodeProvider:

    public static string ParseString(string txt)
    {
        var provider = new Microsoft.CSharp.CSharpCodeProvider();
        var prms = new System.CodeDom.Compiler.CompilerParameters();
        prms.GenerateExecutable = false;
        prms.GenerateInMemory = true;
        var results = provider.CompileAssemblyFromSource(prms, @"
namespace tmp
{
    public class tmpClass
    {
        public static string GetValue()
        {
             return " + "\"" + txt + "\"" + @";
        }
    }
}");
        System.Reflection.Assembly ass = results.CompiledAssembly;
        var method = ass.GetType("tmp.tmpClass").GetMethod("GetValue");
        return method.Invoke(null, null) as string;
    }

使用通配符字典并在字符串中替换它们可能会更好。

答案 1 :(得分:3)

不确定这是否是最简单的方法,但通过引用Microsoft.JScript命名空间,您可以使用javascript eval函数重新解析它。

这是对底部代码的测试

var evalToString = Evaluator.MyStr("test \\r\\n test");

这会将\ r变为回车。

实施

public class Evaluator
{
    public static object MyStr(string statement)
    {
        return _evaluatorType.InvokeMember(
                    "MyStr",
                    BindingFlags.InvokeMethod,
                    null,
                    _evaluator,
                    new object[] { statement }
                 );
    }

    static Evaluator()
    {
        ICodeCompiler compiler;
        compiler = new JScriptCodeProvider().CreateCompiler();

        CompilerParameters parameters;
        parameters = new CompilerParameters();
        parameters.GenerateInMemory = true;

        CompilerResults results;
        results = compiler.CompileAssemblyFromSource(parameters, _jscriptSource);

        Assembly assembly = results.CompiledAssembly;
        _evaluatorType = assembly.GetType("Evaluator.Evaluator");

        _evaluator = Activator.CreateInstance(_evaluatorType);
    }

    private static object _evaluator = null;
    private static Type _evaluatorType = null;
    private static readonly string _jscriptSource =

        @"package Evaluator
        {
           class Evaluator
           {
              public function MyStr(expr : String) : String 
              { 
                 var x;
                 eval(""x='""+expr+""';"");
                 return x;
              }
           }
        }";
}

答案 2 :(得分:3)

Regex.Unescape将是我的首选方法。

答案 3 :(得分:2)

如果你只是想做什么"简单"转义Microsoft site上定义的转义字符,您可以使用此例程并保存导入外部库:

public static class StringExtensions
{
    /* https://msdn.microsoft.com/en-us/library/aa691087(v=vs.71).aspx */
    private readonly static SortedDictionary<char, char> EscapeMap = new SortedDictionary<char, char>
    {
        { '\'', '\'' },
        { '"', '\"' },
        { '\\', '\\' },
        { '0', '\0' },
        { 'a', '\a' },
        { 'b', '\b' },
        { 'f', '\f' },
        { 'n', '\n' },
        { 'r', '\r' },
        { 't', '\t' },
        { 'v', '\v' },
    };

    public static string UnescapeSimple(this string escaped)
    {
        if (escaped == null)
            return escaped;

        var sb = new StringBuilder();

        bool inEscape = false;
        var s = 0;
        for (var i = 0; i < escaped.Length; i++)
        {
            if (!inEscape && escaped[i] ==  '\\')
            {
                inEscape = true;
                continue;
            }

            if (inEscape)
            {
                char mapChar;
                if (EscapeMap.TryGetValue(escaped[i], out mapChar))
                {
                    sb.Append(escaped.Substring(s, i - s - 1));
                    sb.Append(mapChar);

                    s = i + 1;
                }
                inEscape = false;
            }
        }

        sb.Append(escaped.Substring(s));

        return sb.ToString();
    }
}

这是一个证明它的单元测试:

    [TestMethod]
    public void UnescapeSimpleTest()
    {
        var noEscapes = @"This is a test".UnescapeSimple();
        Assert.AreEqual("This is a test", noEscapes, nameof(noEscapes));

        var singleEscape = @"\n".UnescapeSimple();
        Assert.AreEqual("\n", singleEscape, nameof(singleEscape));

        var allEscape = @"\'\""\\\0\a\b\f\n\r\t\v".UnescapeSimple();
        Assert.AreEqual("\'\"\\\0\a\b\f\n\r\t\v", allEscape, nameof(allEscape));

        var textInEscapes = @"\tthis\n\ris\\a\ntest".UnescapeSimple();
        Assert.AreEqual("\tthis\n\ris\\a\ntest", textInEscapes, nameof(textInEscapes));

        var backslashNoEscapes = @"\,\h\qtest".UnescapeSimple();
        Assert.AreEqual(@"\,\h\qtest", backslashNoEscapes, nameof(backslashNoEscapes));

        var emptyStr = "".UnescapeSimple();
        Assert.AreEqual("", emptyStr, nameof(emptyStr));

        // Prove Enviroment.NewLine is "\r\n" and not "\n\r" (Windows PC)
        var newLine = @"\r\n".UnescapeSimple();
        Assert.AreEqual(Environment.NewLine, newLine, nameof(newLine));

        // Double check prior test (Windows PC)
        var newLineWrong = @"\n\r".UnescapeSimple();
        Assert.AreNotEqual(Environment.NewLine, newLineWrong, nameof(newLineWrong));
    }

随意调整EscapeMap或重命名函数UnescapeSimple(我知道这很尴尬)。

请注意,此解决方案不处理Unicode转义字符或十六进制或八进制,它只处理简单的单字符。