我正在努力寻找能够将预期文本与所输入文本的当前状态进行比较的文本比较工具或算法。 我会让一个实验者在他眼前写下他所拥有的文字。我的想法是在输入内容时比较文本的当前状态和预期的文本。这样我想知道主题何时出错以及出错了什么(我也希望找到不在结果文本中但在中间文本中有一段时间的错误)。 有人能指出我的方向吗?
更新#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
答案 0 :(得分:0)
根据您的输入 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
以下代码执行以下操作:
steps
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
在这里,你有
current
要比较expected
和Shift + 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的评估以及新的统一错误指标”中提出了三个主要的新指标:“总错误率”, “已纠正的错误率”和“未纠正的错误率”。自从本文发表以来,这些指标已经很完善。这些是我一直在寻找的指标。
如果您要尝试做与我所做的类似的事情,例如比较这两种方法在不同输入设备上的书写性能。