繁体   English   中英

存储最后N次击键

[英]Storing the last N keystrokes

在这里做什么我有点不知所措。 我希望某些键击序列执行某些操作。

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

所以说我有2个序列:

yes
no

当我输入时,我的击键历史如下所示:

a
ab
abc
abcn
abcno

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

它还需要使用以下序列:

year
yell

和输入如:

yeayell

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

我的击键用Keys枚号表示。

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

这是一个概念验证,允许您使用任何字符序列集合。 我假设你只匹配字符(而不是其他键,如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()));
    }
}

一个简单的状态机应该运行良好。

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

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 

您可以使用循环缓冲区:

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;

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

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

创建一个CircularBuffer<T>泛型容器。

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

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

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

如果i = N ,则Add方法递增i ,将其包装为0并将值存储在数组中的适当位置。 这应该允许您向缓冲区添加值,同时仅在内存中保留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方法,看它是否包含您寻找的序列。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM