存储最后N次击键

时间:2012-02-04 20:42:27

标签: c# .net

我在这里做什么有点不知所措。我想让某些按键序列执行某些操作。

我基本上需要存储最后N次击键,当按下某个键时,查找与最近一次击键相匹配的序列。

所以说我有2个序列:

yes
no

当我输入时,我的按键历史如下:

a
ab
abc
abcn
abcno

此时它应识别序列no并执行相应的操作。

还需要使用以下序列:

year
yell

并输入如:

yeayell

键序列的长度是有限的,因此可以使用类似循环缓冲区的东西丢弃旧键击,在这种情况下,最佳大小为3。

我的击键用Keys枚号表示。

我应该使用哪些数据结构或算法来存储最后N次击键并在最后找到序列?

5 个答案:

答案 0 :(得分:1)

这是一个概念验证,允许您使用任何字符序列集合。我假设你只是匹配字符(而不是其他键,如Keys.Left)。

// Initialize the collection of strings to be matched against here.
string[] stringSequences = new string[] { "yes", "no", "hello" };
int maxLength = stringSequences.Max(s => s.Length);

// The buffer to hold the sequence of the last N characters.
string buffer = "";

while (true)
{
    // Read the next character, and append it to the end of the buffer.
    ConsoleKeyInfo next = Console.ReadKey();
    buffer += next.KeyChar;

    // If the buffer has exceeded our maximum length, 
    // trim characters from its start.
    if (buffer.Length > maxLength)
        buffer = buffer.Substring(1);

    // Check whether the last n characters of the buffer
    // correspond to any of the sequences.
    string match = stringSequences.FirstOrDefault(s => buffer.EndsWith(s));
    if (match != null)
    {
        // Match! Perform any custom processing here.
        Console.WriteLine(Environment.NewLine + "Match: " + match);
    }
}

修改:适合使用密钥。

我无法轻易针对Keys进行测试,因此我与ConsoleKey进行了对话;但是,翻译代码应该不会太难。

// Initialize the collection of key sequences to be matched against here.
ConsoleKey[][] keysSequences = new ConsoleKey[][]
{ 
    new ConsoleKey[] { ConsoleKey.Y, ConsoleKey.E, ConsoleKey.S },
    new ConsoleKey[] { ConsoleKey.N, ConsoleKey.O },
    new ConsoleKey[] { ConsoleKey.H, ConsoleKey.E, ConsoleKey.L, ConsoleKey.L, ConsoleKey.O },
};
int maxLength = keysSequences.Max(ks => ks.Length);

// The buffer to hold the sequence of the last N keys.
List<ConsoleKey> buffer = new List<ConsoleKey>();

while (true)
{
    // Read the next key, and append it to the end of the buffer.
    ConsoleKeyInfo next = Console.ReadKey();
    buffer.Add(next.Key);

    // If the buffer has exceeded our maximum length, 
    // trim keys from its start.
    if (buffer.Count > maxLength)
        buffer.RemoveAt(0);

    // Check whether the last n keys of the buffer
    // correspond to any of the sequences.
    ConsoleKey[] match = keysSequences.FirstOrDefault(ks => 
        buffer.Skip(buffer.Count - ks.Length).SequenceEqual(ks));
    if (match != null)
    {
        // Match! Perform any custom processing here.
        Console.WriteLine(Environment.NewLine + "Match: " + 
            string.Concat(match.Select(k => k.ToString()).ToArray()));
    }
}

答案 1 :(得分:0)

简单的state machine应该效果很好。

它可以重置任何不遵守规则的输入。

enum States
{
 initial,
 y,
 e,
 s,
 n,
 o
}

if(char == 'n' && state == states.Initial)
{
  state = States.n;
}

if(char == 'o' && state == states.n)
{
  state = States.o;
}

... // etc for y, e, s - resetting to `Initial` where needed

... // Check for states o or s 

答案 2 :(得分:0)

您可以使用循环缓冲区:

char[] buf = new char[3];
int pos = 0;

// on key press
buf[pos] = key;

if (buf[pos] == 'o' && buf[(pos + 2) % 3] == 'n')
    No();

if (buf[pos] == 's' && buf[(pos + 2) % 3] == 'e' && buf[(pos + 1) % 3] == 'y')
    Yes();

pos = (pos + 1) % 3;

答案 3 :(得分:0)

快速而简单的方法是使用Rolling hash

答案 4 :(得分:0)

不一定是最佳的,但也许是最简单和可重复使用的:

创建CircularBuffer<T>通用容器。

这在构造函数N中占用一个参数,即缓冲区中元素的最大数量。

该类有一个T数组N元素和一个索引变量i,保存最后一个添加值的索引,将其初始化为-1。

CircularBuffer有两种方法,Add(T)和ToString()。

Add方法增加i,如果0 = i将其包装到N,并将值存储在数组中的适当位置。这应该允许您向缓冲区添加值,同时仅将N个值保留在内存中。

ToString将输出一个等于缓冲区中存储的字符串。

对于N = 4,假设你有这个数组:

                         a b c d

the indices are          0 1 2 3

the last added value is      ^

`ToString` should return 'dabc'

此处的包装逻辑是用户的练习。

每按一次键,将其添加到循环缓冲区,调用其ToString方法,看它是否包含您寻找的序列。