每次击键后进行文本比较的工具/算法

时间:2017-09-21 09:35:21

标签: algorithm text time comparison typing

我正在努力寻找能够将预期文本与所输入文本的当前状态进行比较的文本比较工具或算法。 我会让一个实验者在他眼前写下他所拥有的文字。我的想法是在输入内容时比较文本的当前状态和预期的文本。这样我想知道主题何时出错以及出错了什么(我也希望找到不在结果文本中但在中间文本中有一段时间的错误)。 有人能指出我的方向吗?

更新#1 我可以访问csv格式的输入数据: 这是我输入“foOBar”的示例输出数据。每一行都有表格(时间戳,键,按/释放)

17293398.576653,F,P
17293398.6885,F,R
17293399.135282,LeftShift,P
17293399.626881,LeftShift,R
17293401.313254,O,P
17293401.391732,O,R
17293401.827314,LeftShift,P
17293402.073046,O,P
17293402.184859,O,R
17293403.178612,B,P
17293403.301748,B,R
17293403.458137,LeftShift,R
17293404.966193,A,P
17293405.077869,A,R
17293405.725405,R,P
17293405.815159,R,R

2 个答案:

答案 0 :(得分:0)

在Python中

根据您的输入 csv 文件(我称之为keyboard_records.csv

17293398.576653,F,P
17293398.6885,F,R
17293399.135282,LeftShift,P
17293399.626881,LeftShift,R
17293401.313254,O,P
17293401.391732,O,R
17293401.827314,LeftShift,P
17293402.073046,O,P
17293402.184859,O,R
17293403.178612,B,P
17293403.301748,B,R
17293403.458137,LeftShift,R
17293404.966193,A,P
17293405.077869,A,R
17293405.725405,R,P
17293405.815159,R,R

以下代码执行以下操作:

  1. 阅读其内容并将其存储在名为steps
  2. 的列表中
  3. step中的每个steps都会识别发生的事情
    • 如果是shift按下或发布设置了相应的标记(shift_on
    • 如果是按下箭头,则移动cursor(我们插入字符的current索引) - 如果光标位于字符串的开头或末尾,则不应该移动,这就是那些min()max()
    • 的原因
    • 如果是字母/数字/符号,则会在curret cursor位置添加cursor并递增import csv steps = [] # list of all actions performed by user expected = "Hello" with open("keyboard.csv") as csvfile: for row in csv.reader(csvfile, delimiter=','): steps.append((float(row[0]), row[1], row[2])) # Now we parse the information current = [] # text written by the user shift_on = False # is shift pressed cursor = 0 # where is the cursor in the current text for step in steps: time, key, action = step if key == 'LeftShift': if action == 'P': shift_on = True else: shift_on = False continue if key == 'LeftArrow' and action == 'P': cursor = max(0, cursor-1) continue if key == 'RightArrow' and action == 'P': cursor = min(len(current), cursor+1) continue if action == 'P': if shift_on is True: current.insert(cursor, key.upper()) else: current.insert(cursor, key.lower()) cursor += 1 # Now you can join current into a string # and compare current with expected print(''.join(current)) # printing current (just to see what's happening) else: # What to do when a key is released? # Depends on your needs... continue
  4. 在这里,你有

    current

    要比较expectedShift + 6 = &,请查看here

    注意:通过使用上面的代码和更多标志,您可以使其识别符号。这取决于您的键盘。在我的AltGr + E = €Ctrl + Shift + AltGr + è = {compare。我认为这是一个很好的开始。

    更新

    比较2个文本并不是一项艰巨的任务,您可以在网络上find tons about it页面{{3}}。 无论如何,我想向您展示一个面向对象的问题方法,所以我添加了我之前在第一个解决方案中省略的class UserText: # Initialize UserText: # - empty text # - cursor at beginning # - shift off def __init__(self, expected): self.expected = expected self.letters = [] self.cursor = 0 self.shift = False # compares a and b and returns a # list containing the indices of # mismatches between a and b def compare(a, b): err = [] for i in range(min(len(a), len(b))): if a[i] != b[i]: err.append(i) return err # Parse a command given in the # form (time, key, action) def parse(self, command): time, key, action = command output = "" if action == 'P': if key == 'LeftShift': self.shift = True elif key == 'LeftArrow': self.cursor = max(0, self.cursor - 1) elif key == 'RightArrow': self.cursor = min(len(self.letters), self.cursor + 1) else: # Else, a letter/number was pressed. Let's # add it to self.letters in cursor position if self.shift is True: self.letters.insert(self.cursor, key.upper()) else: self.letters.insert(self.cursor, key.lower()) self.cursor += 1 ########## COMPARE WITH EXPECTED ########## output += "Expected: \t" + self.expected + "\n" output += "Current: \t" + str(self) + "\n" errors = UserText.compare(str(self), self.expected[:len(str(self))]) output += "\t\t" i = 0 for e in errors: while i != e: output += " " i += 1 output += "^" i += 1 output += "\n[{} errors at time {}]".format(len(errors), time) return output else: if key == 'LeftShift': self.shift = False return output def __str__(self): return "".join(self.letters) import csv steps = [] # list of all actions performed by user expected = "foobar" with open("keyboard.csv") as csvfile: for row in csv.reader(csvfile, delimiter=','): steps.append((float(row[0]), row[1], row[2])) # Now we parse the information ut = UserText(expected) for step in steps: print(ut.parse(step)) 部分。

    这仍然是一个粗略的代码,没有对输入的主要控制。但是,正如你所问,这是指向你的方向

    csv

    上面Expected: foobar Current: f [0 errors at time 17293398.576653] Expected: foobar Current: fo [0 errors at time 17293401.313254] Expected: foobar Current: foO ^ [1 errors at time 17293402.073046] Expected: foobar Current: foOB ^^ [2 errors at time 17293403.178612] Expected: foobar Current: foOBa ^^ [2 errors at time 17293404.966193] Expected: foobar Current: foOBar ^^ [2 errors at time 17293405.725405] 文件的输出是:

    public class Rectangles extends JPanel {
    
        List<Rectangle2D> rectangles = new ArrayList<Rectangle2D>();
        {
            // x,y,w,h
            rectangles.add(new Rectangle2D.Float(300, 50, 50, 50));
    
            rectangles.add(new Rectangle2D.Float(300, 50, 20, 50));
    
            rectangles.add(new Rectangle2D.Float(100, 100, 100, 50));
    
            rectangles.add(new Rectangle2D.Float(120, 200, 50, 50));
    
            rectangles.add(new Rectangle2D.Float(150, 130, 100, 100));
    
            rectangles.add(new Rectangle2D.Float(0, 100, 100, 50));
    
            for (int i = 0; i < 10; i++) {
                for (int j = 0; j < 10; j++) {
                    rectangles.add(new Rectangle2D.Float(i * 40, j * 40, 20, 20));
                }
            }
        }
    
        List<Rectangle2D> rectanglesToDraw;
    
        protected void reset() {
            rectanglesToDraw = rectangles;
    
            this.repaint();
        }
    
        private List<Rectangle2D> findIntersections(Rectangle2D rect, List<Rectangle2D> rectList) {
    
            ArrayList<Rectangle2D> intersections = new ArrayList<Rectangle2D>();
    
            for (Rectangle2D intersectingRect : rectList) {
                if (!rect.equals(intersectingRect) && intersectingRect.intersects(rect)) {
                    intersections.add(intersectingRect);
                }
            }
    
            return intersections;
        }
    
        protected void fix() {
            rectanglesToDraw = new ArrayList<Rectangle2D>();
    
            for (Rectangle2D rect : rectangles) {
                Rectangle2D copyRect = new Rectangle2D.Double();
                copyRect.setRect(rect);
                rectanglesToDraw.add(copyRect);
            }
    
            // Find the center C of the bounding box of your rectangles.
            Rectangle2D surroundRect = surroundingRect(rectanglesToDraw);
            Point center = new Point((int) surroundRect.getCenterX(), (int) surroundRect.getCenterY());
    
            int movementFactor = 5;
    
            boolean hasIntersections = true;
    
            while (hasIntersections) {
    
                hasIntersections = false;
    
                for (Rectangle2D rect : rectanglesToDraw) {
    
                    // Find all the rectangles R' that overlap R.
                    List<Rectangle2D> intersectingRects = findIntersections(rect, rectanglesToDraw);
    
                    if (intersectingRects.size() > 0) {
    
                        // Define a movement vector v.
                        Point movementVector = new Point(0, 0);
    
                        Point centerR = new Point((int) rect.getCenterX(), (int) rect.getCenterY());
    
                        // For each rectangle R that overlaps another.
                        for (Rectangle2D rPrime : intersectingRects) {
                            Point centerRPrime = new Point((int) rPrime.getCenterX(), (int) rPrime.getCenterY());
    
                            int xTrans = (int) (centerR.getX() - centerRPrime.getX());
                            int yTrans = (int) (centerR.getY() - centerRPrime.getY());
    
                            // Add a vector to v proportional to the vector between the center of R and R'.
                            movementVector.translate(xTrans < 0 ? -movementFactor : movementFactor,
                                    yTrans < 0 ? -movementFactor : movementFactor);
    
                        }
    
                        int xTrans = (int) (centerR.getX() - center.getX());
                        int yTrans = (int) (centerR.getY() - center.getY());
    
                        // Add a vector to v proportional to the vector between C and the center of R.
                        movementVector.translate(xTrans < 0 ? -movementFactor : movementFactor,
                                yTrans < 0 ? -movementFactor : movementFactor);
    
                        // Move R by v.
                        rect.setRect(rect.getX() + movementVector.getX(), rect.getY() + movementVector.getY(),
                                rect.getWidth(), rect.getHeight());
    
                        // Repeat until nothing overlaps.
                        hasIntersections = true;
                    }
    
                }
            }
            this.repaint();
        }
    
        private Rectangle2D surroundingRect(List<Rectangle2D> rectangles) {
    
            Point topLeft = null;
            Point bottomRight = null;
    
            for (Rectangle2D rect : rectangles) {
                if (topLeft == null) {
                    topLeft = new Point((int) rect.getMinX(), (int) rect.getMinY());
                } else {
                    if (rect.getMinX() < topLeft.getX()) {
                        topLeft.setLocation((int) rect.getMinX(), topLeft.getY());
                    }
    
                    if (rect.getMinY() < topLeft.getY()) {
                        topLeft.setLocation(topLeft.getX(), (int) rect.getMinY());
                    }
                }
    
                if (bottomRight == null) {
                    bottomRight = new Point((int) rect.getMaxX(), (int) rect.getMaxY());
                } else {
                    if (rect.getMaxX() > bottomRight.getX()) {
                        bottomRight.setLocation((int) rect.getMaxX(), bottomRight.getY());
                    }
    
                    if (rect.getMaxY() > bottomRight.getY()) {
                        bottomRight.setLocation(bottomRight.getX(), (int) rect.getMaxY());
                    }
                }
            }
    
            return new Rectangle2D.Double(topLeft.getX(), topLeft.getY(), bottomRight.getX() - topLeft.getX(),
                    bottomRight.getY() - topLeft.getY());
        }
    
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
    
            for (Rectangle2D entry : rectanglesToDraw) {
                g2d.setStroke(new BasicStroke(1));
                // g2d.fillRect((int) entry.getX(), (int) entry.getY(), (int) entry.getWidth(),
                // (int) entry.getHeight());
                g2d.draw(entry);
            }
    
        }
    
        protected static void createAndShowGUI() {
            Rectangles rects = new Rectangles();
    
            rects.reset();
    
            JFrame frame = new JFrame("Rectangles");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new BorderLayout());
            frame.add(rects, BorderLayout.CENTER);
    
            JPanel buttonsPanel = new JPanel();
    
            JButton fix = new JButton("Fix");
    
            fix.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    rects.fix();
    
                }
            });
    
            JButton resetButton = new JButton("Reset");
    
            resetButton.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    rects.reset();
                }
            });
    
            buttonsPanel.add(fix);
            buttonsPanel.add(resetButton);
    
            frame.add(buttonsPanel, BorderLayout.SOUTH);
    
            frame.setSize(400, 400);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    createAndShowGUI();
    
                }
            });
        }
    
    }
    

答案 1 :(得分:0)

大约一年前,我找到了自己的问题的解决方案。现在我有时间与您分享:

R. William Soukoreff和I.Scott MacKenzie在其2003年的论文“用于文本输入研究的指标:对MSD和KSPC的评估以及新的统一错误指标”中提出了三个主要的新指标:“总错误率”, “已纠正的错误率”和“未纠正的错误率”。自从本文发表以来,这些指标已经很完善。这些是我一直在寻找的指标。

如果您要尝试做与我所做的类似的事情,例如比较这两种方法在不同输入设备上的书写性能。