将用户输入与预制文本链进行比较

时间:2014-12-18 19:31:06

标签: c# string linq input

我在使用Visual C#创建一个非常基本的结构分析软件的第一步。

我决定让它基于控制台(没有用户界面)。因此,获取用户输入的唯一方法是通过字符和字符串。

想象一下,用户想要创建一个2D条形元素。她需要指定该条的初始点,最终点和名称。我希望语法如下:

“CREATE bar NAMED(bar_name)FIRST(first_point)LAST(last_point)”

其中:

  • (bar_name)是条形图的名称,由用户决定。设(object_name)=“bar_A”(字符串类型)。
  • (first_point)将是该栏的起始点。由于我们正在创建2D条,(first_point)应该是1x2向量,用户应在括号之间输入。例如,(first_point)=(0,0)
  • (last_point)将是该栏的最后一点。与(first_point)相同的类型和语法。

我只是想知道是否有任何简单的方法来实现字符串比较任务,比如将用户的输入与预制命令进行比较。

当然不会忘记用户的输入清洁任务。

我知道这里有很多可能的解决方案。也许使用LINQ。也许只是使用String对象。我只想知道最有效的方法,其中有效的方法是:

  • 处理用户查询的速度越快越好;
  • 代码行越少越好;和
  • 进行彻底的查询清理任务。

最后一点非常重要,因为有些用户输入如下:

“CREATE bar NAMED bar_a FISRT (0,0)LAST(0,1)”

请注意,用户提交了拼写错误(FISRT而不是FIRST),查询不应该运行。

由于

2 个答案:

答案 0 :(得分:1)

标记化是一种方法,但如果你不打算支持太多的命令和参数,你应该看看Regexes。

Regex regex = new Regex(@"^CREATE bar NAMED (?<BarName>[A-Za-z0-9-_]*) FIRST (?<FirstPoint>\([0-9]+\|[0-9]+\)) LAST (?<LastPoint>\([0-9]+\|[0-9]+\)$");
Match match = regex.Match("create bar named bar_a first (0,0) last (0,1)", RegexOptions.IgnoreCase);
if (match.Success)
{
    var name = match.Groups["BarName"].Value;
    // and so on for other matches
}

答案 1 :(得分:1)

好的,我创建了一个适合您的简单解析器,如果需要,您可以轻松扩展。

首先创建一个新的控制台应用程序。添加名为Tokenizer.cs的新类文件。这个文件是由我在上面的评论中链接到你的TokenIcer项目自动生成的。让Tokenizer.cs看起来像这样:

public class TokenParser
{
    private readonly Dictionary<Tokens, string> _tokens;
    private readonly Dictionary<Tokens, MatchCollection> _regExMatchCollection;
    private string _inputString;
    private int _index;

    public enum Tokens
    {
        UNDEFINED = 0,
        CREATE = 1,
        FIRST = 2,
        LAST = 3,
        BAR = 4,
        NAMED = 5,
        BAR_NAME = 6,
        WHITESPACE = 7,
        LPAREN = 8,
        RPAREN = 9,
        COMMA = 10,
        NUMBER = 11
    }

    public string InputString
    {
        set
        {
            _inputString = value;
            PrepareRegex();
        }
    }

    public TokenParser()
    {
        _tokens = new Dictionary<Tokens, string>();
        _regExMatchCollection = new Dictionary<Tokens, MatchCollection>();
        _index = 0;
        _inputString = string.Empty;

        _tokens.Add(Tokens.CREATE, "[Cc][Rr][Ee][Aa][Tt][Ee]");
        _tokens.Add(Tokens.FIRST, "[Ff][Ii][Rr][Ss][Tt]");
        _tokens.Add(Tokens.LAST, "[Ll][Aa][Ss][Tt]");
        _tokens.Add(Tokens.BAR, "[Bb][Aa][Rr][ \\t]");
        _tokens.Add(Tokens.NAMED, "[Nn][Aa][Mm][Ee][Dd]");
        _tokens.Add(Tokens.BAR_NAME, "[A-Za-z_][a-zA-Z0-9_]*");
        _tokens.Add(Tokens.WHITESPACE, "[ \\t]+");
        _tokens.Add(Tokens.LPAREN, "\\(");
        _tokens.Add(Tokens.RPAREN, "\\)");
        _tokens.Add(Tokens.COMMA, "\\,");
        _tokens.Add(Tokens.NUMBER, "[0-9]+");
    }

    private void PrepareRegex()
    {
        _regExMatchCollection.Clear();
        foreach (KeyValuePair<Tokens, string> pair in _tokens)
        {
            _regExMatchCollection.Add(pair.Key, Regex.Matches(_inputString, pair.Value));
        }
    }

    public void ResetParser()
    {
        _index = 0;
        _inputString = string.Empty;
        _regExMatchCollection.Clear();
    }

    public Token GetToken()
    {
        if (_index >= _inputString.Length)
            return null;

        foreach (KeyValuePair<Tokens, MatchCollection> pair in _regExMatchCollection)
        {
            foreach (Match match in pair.Value)
            {
                if (match.Index == _index)
                {
                    _index += match.Length;
                    return new Token(pair.Key, match.Value);
                }

                if (match.Index > _index)
                {
                    break;
                }
            }
        }
        _index++;
        return new Token(Tokens.UNDEFINED, string.Empty);
    }

    public PeekToken Peek()
    {
        return Peek(new PeekToken(_index, new Token(Tokens.UNDEFINED, string.Empty)));
    }

    public PeekToken Peek(PeekToken peekToken)
    {
        int oldIndex = _index;

        _index = peekToken.TokenIndex;

        if (_index >= _inputString.Length)
        {
            _index = oldIndex;
            return null;
        }

        foreach (KeyValuePair<Tokens, string> pair in _tokens)
        {
            var r = new Regex(pair.Value);
            Match m = r.Match(_inputString, _index);

            if (m.Success && m.Index == _index)
            {
                _index += m.Length;
                var pt = new PeekToken(_index, new Token(pair.Key, m.Value));
                _index = oldIndex;
                return pt;
            }
        }
        var pt2 = new PeekToken(_index + 1, new Token(Tokens.UNDEFINED, string.Empty));
        _index = oldIndex;
        return pt2;
    }
}

public class PeekToken
{
    public int TokenIndex { get; set; }

    public Token TokenPeek { get; set; }

    public PeekToken(int index, Token value)
    {
        TokenIndex = index;
        TokenPeek = value;
    }
}

public class Token
{
    public TokenParser.Tokens TokenName { get; set; }

    public string TokenValue { get; set; }

    public Token(TokenParser.Tokens name, string value)
    {
        TokenName = name;
        TokenValue = value;
    }
}

Program.cs中,请将其显示如下:

class Program
{
    private class Bar
    {
        public string Name { get; set; }
        public int FirstX { get; set; }
        public int FirstY { get; set; }
        public int LastX { get; set; }
        public int LastY { get; set; }
    }

    static void Main(string[] args)
    {
        const string commandCreateBar1 = "CREATE bar NAMED bar_a FIRST(5,10) LAST (15,20)";
        const string commandCreateBar2 = "CREATE bar NAMED MyFooBar FIRST(25  ,  31)    LAST   (153 ,210)";
        const string commandCreateBar3 = "CREATE    bar   NAMED    MySpaceyFooBar FIRST(0,0)  LAST (12,39)";

        Bar bar1 = ParseCreateBar(commandCreateBar1);
        PrintBar(bar1);

        Bar bar2 = ParseCreateBar(commandCreateBar2);
        PrintBar(bar2);

        Bar bar3 = ParseCreateBar(commandCreateBar3);
        PrintBar(bar3);
    }

    private static void PrintBar(Bar bar)
    {
        Console.WriteLine("A new bar was Created! \"{0}\" ({1}, {2}) ({3}, {4})", bar.Name, bar.FirstX, bar.FirstY, bar.LastX, bar.LastY);
    }

    private static Bar ParseCreateBar(string commandLine)
    {
        var bar = new Bar();
        var parser = new TokenParser { InputString = commandLine };

        Expect(parser, TokenParser.Tokens.CREATE);
        Expect(parser, TokenParser.Tokens.BAR);
        Expect(parser, TokenParser.Tokens.NAMED);
        Token token = Expect(parser, TokenParser.Tokens.BAR_NAME);
        bar.Name = token.TokenValue;
        Expect(parser, TokenParser.Tokens.FIRST);
        Expect(parser, TokenParser.Tokens.LPAREN);
        token = Expect(parser, TokenParser.Tokens.NUMBER);
        bar.FirstX = int.Parse(token.TokenValue);
        Expect(parser, TokenParser.Tokens.COMMA);
        token = Expect(parser, TokenParser.Tokens.NUMBER);
        bar.FirstY = int.Parse(token.TokenValue);
        Expect(parser, TokenParser.Tokens.RPAREN);
        Expect(parser, TokenParser.Tokens.LAST);
        Expect(parser, TokenParser.Tokens.LPAREN);
        token = Expect(parser, TokenParser.Tokens.NUMBER);
        bar.LastX = int.Parse(token.TokenValue);
        Expect(parser, TokenParser.Tokens.COMMA);
        token = Expect(parser, TokenParser.Tokens.NUMBER);
        bar.LastY = int.Parse(token.TokenValue);
        Expect(parser, TokenParser.Tokens.RPAREN);

        return bar;
    }

    private static Token Expect(TokenParser parser, TokenParser.Tokens expectedToken)
    {
        EatWhiteSpace(parser);
        Token token = parser.GetToken();
        if (token != null && token.TokenName != expectedToken)
        {
            Console.WriteLine("Expected Token " + expectedToken);
            Environment.Exit(0);
        }

        if (token == null)
        {
            Console.WriteLine("Unexpected end of input!");
            Environment.Exit(0);
        }

        return token;
    }

    private static void EatWhiteSpace(TokenParser parser)
    {
        while (parser.Peek() != null && parser.Peek().TokenPeek != null && 
            parser.Peek().TokenPeek.TokenName == TokenParser.Tokens.WHITESPACE)
        {
            parser.GetToken();
        }
    }
}

如您所见,我创建了3个测试场景。请注意,忽略所有空格。如果您想严格控制空格,可以将EatWhiteSpace函数修改为严格。

如果你愿意,我有一个简单的表达式解析器,我也可以投入到这个代码中,这样你就可以拥有CREATE bar NAMED bar_a FIRST(3+2, 7*8 + 12) LAST (150-100, 12-3*2)之类的命令。我有一个简单的表达式解析器我使用TokenIcer做了一段时间我可以投入。它可以解析任何数学表达式并支持括号,加,减,乘和除。