制限時間内のキー入力検査
Posted feedbacks - Nested
Flatten Hiddencurses初めて使いました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | require 'curses'
require 'timeout'
def inputChecker(n, str)
Curses.init_screen
Curses.setpos(0, 0)
Curses.addstr("input(#{str}) => ")
Curses.refresh
result = nil
begin
input = ""
c = Curses.getch.chr
timeout(n){
until c == "\n"
input += c
c = Curses.getch.chr
end
result = input == str ? "OK" : "NG"
}
rescue Timeout::Error
result = "TIME OUT"
end
Curses.setpos(1, 0)
Curses.addstr("result => #{result}")
Curses.refresh
end
inputChecker(5, "ABCDEF")
|
僕も6番思いっきりシカトでした。 3回連続実行も。これで大丈夫と思うのですが。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | require 'curses'
def inputChecker(n, str)
Curses.addstr("input(#{str}) => ")
Curses.refresh
input = ""
timeout = false
until (c = Curses.getch.chr) == "\n"
timer ||= Thread.new{ sleep(n); timeout = true }
input += c
end
result = timeout ? "TIME OUT" : input == str ? "OK" : "NG"
Curses.setpos(Curses.stdscr.cury+1, 0)
Curses.addstr("result => #{result}")
Curses.refresh
Curses.setpos(Curses.stdscr.cury+2, 0)
end
if __FILE__ == $0
Curses.init_screen
3.times{ inputChecker(5, "ABCDEF") }
Curses.close_screen
end
|
Squeak Smalltalk で。
残念ながら Squeak Smalltalk には自身の標準入力やホスト OS のそれとの連携といった概念が(少なくともデフォルト状態では…)ないので、マウスポインタ付近にプロンプトを表示して入力を促すことで対応させていただきました。標準入出力のしかたを問うことが題意でしたら、どうぞあしからず。
残念ながら Squeak Smalltalk には自身の標準入力やホスト OS のそれとの連携といった概念が(少なくともデフォルト状態では…)ないので、マウスポインタ付近にプロンプトを表示して入力を促すことで対応させていただきました。標準入出力のしかたを問うことが題意でしたら、どうぞあしからず。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | | inputChecker |
inputChecker := [:timeLimitSec :expecStr |
| char stream start resultMsg initPosition |
stream := WriteStream with: 'input(', expecStr, ') => '.
initPosition := stream position.
start := nil.
[ stream contents, ' ' displayAt: Sensor cursorPoint + 10 asPoint.
[Sensor keyboardPressed] whileFalse.
(char := Sensor keyboard) = Character cr
] whileFalse: [
start ifNil: [start := Time millisecondClockValue].
char == Character backspace
ifTrue: [stream position: (stream position - 1 max: initPosition)]
ifFalse: [stream nextPut: char]].
resultMsg := (Time millisecondClockValue - start) / 1.0e3 <= timeLimitSec
ifFalse: ['TIME OUT']
ifTrue: [(stream contents last: expecStr size) = expecStr
ifTrue: ['OK'] ifFalse: ['NG']].
#result -> resultMsg].
inputChecker value: 5 value: 'ABCDEF'
|
標準ではない System.Timeout モジュールを使う
see: System.Timeout.hs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | module Main (main) where
import System.Environment
import System.IO
import System.Timeout
main :: IO ()
main = do { prog <- getProgName
; args <- getArgs
; case args of
t:str:_ -> loop 3 (inputChecker (read t) str)
_ -> usage prog
}
loop :: Int -> IO () -> IO ()
loop 0 _ = return ()
loop n f = f >> loop (n-1) f
usage :: String -> IO ()
usage prog = hPutStrLn stderr $ prog ++ " <timeout> <string>"
inputChecker :: Int -> String -> IO ()
inputChecker t s
= do { hPutStr stderr $ "input(" ++ s ++ ") => "
; hSetBuffering stdin NoBuffering
; c <- hGetChar stdin
; ms <- timeout (t*1000000) (hGetLine stdin)
; case ms of
Nothing -> hPutStrLn stdout "\n result => TIME OUT" >> hFlush stdout
Just s' -> if s == (c:s') then hPutStrLn stdout " result => OK" >> hFlush stdout
else hPutStrLn stdout " result => NG" >> hFlush stdout
}
{-
*Main> :main 5 ABCDEF
input(ABCDEF) => ABCDEF
result => OK
input(ABCDEF) => A
result => TIME OUT
input(ABCDEF) => PQR
result => NG
*Main> _
-}
|
補足です。 System.Timeoutモジュールは、次のghcのバージョン6.8.xのbaseパッケージに追加されるようです。
see: System.Timeout
ああーっ。仕様6を満たしてなかった。 timeout というので短絡的に仕様6が頭から 飛んじゃったみたい。 仕様6があるほうがやさしいかも。。。 というわけで変更。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | import System.Environment
import System.IO
import System.Time
main :: IO ()
main = do { prog <- getProgName
; args <- getArgs
; case args of
t:str:_ -> loop 3 (inputChecker (read t) str)
_ -> usage prog
}
loop :: Int -> IO () -> IO ()
loop 0 _ = return ()
loop n f = f >> loop (n-1) f
usage :: String -> IO ()
usage prog = hPutStrLn stderr $ prog ++ " <timeout> <string>"
inputChecker :: Int -> String -> IO ()
inputChecker t s
= do { hPutStr stderr $ "input(" ++ s ++ ") => "
; hSetBuffering stdin NoBuffering
; c <- hGetChar stdin
; start <- getClockTime
; s' <- hGetLine stdin
; end <- getClockTime
; let interval = diffClockTimes end start
; if interval > noTimeDiff {tdSec = t}
then hPutStrLn stdout " result => TIME OUT" >> hFlush stdout
else if s == (c:s')
then hPutStrLn stdout " result => OK" >> hFlush stdout
else hPutStrLn stdout " result => NG" >> hFlush stdout
}
{-
*Main> :main 5 ABCDEF
input(ABCDEF) => ABCDEF
result => OK
input(ABCDEF) => ABC
result => NG
input(ABCDEF) => ABCDEF
result => TIME OUT
-}
|
これも標準入出力ではありません。ブラウザ上でキーイベントを受けて、ブラウザ上に結果を表示します。
標準入出力を使うことが必須の要件でしたらゴメンナサイです。
IEとFirefoxのみで確認してます。
標準入出力を使うことが必須の要件でしたらゴメンナサイです。
IEとFirefoxのみで確認してます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | function InputChecker(timeout, s) {
var start;
var input = [];
var prompt = "input("+ s + ")=>"
var pre = document.createElement("pre");
pre.innerHTML = prompt;
document.body.appendChild(pre);
function result() {
var time = new Date().getTime() - start.getTime();
if(time > timeout * 1000) return "TIMEOUT";
else if(s == input) return "OK";
else return "NG"
}
var f = function(e) {
e = e || event;
if(!start) start = new Date();
if(e.keyCode == 13) { // Enter
pre.innerHTML += "<br/>result=>" + result() + "<br/>" + prompt;
input = "";
start = undefined;
} else if(e.charCode == 0 && e.keyCode) { // for firefox. ignore BS, Del, .. etc.
} else {
var c = String.fromCharCode(e.keyCode || e.charCode);
pre.innerHTML += c;
input += c;
}
}
if(document.attachEvent) document.attachEvent("onkeypress", f);
else if(document.addEventListener) document.addEventListener("keypress", f, false);
else document.onkeypress = f;
}
new InputChecker(5, "ABCDEF");
|
<form>を使えばHTMLでもいけるかなと思っていたのですが、まさかそのままで実現するとは思いつきませんでした~。
やってみてびっくり、まるでコンソールのように動きますね(笑
ども、raynstardです。 あくまで管理人さんの意志を優先しますが 僕がお題を投稿したときの意図としては 「時間計測はあくまでユーザーが入力を開始してからです。」を どう実現するかを気にしていました。 なのでキー入力をトリガにして 非同期的な時間計測が実現されていれば 良いかなーなんて考えていました。 お題投稿した時点で、getche()を使えるのが C,python,RUBYくらいしか見つからなかったので。。。
ちょっと冗漫ですが、こんなところで。 # try節からreturnしてもfinally通るんだっけかな。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import termios, sys, tty, select
def InputChecker(N, S):
sys.stdout.write('input(%s) => ' % S)
sys.stdout.flush()
fd = sys.stdin.fileno()
poll = select.poll()
poll.register(fd, select.POLLIN)
attr = termios.tcgetattr(fd)
tty.setcbreak(fd)
s = ''
flg = False
try:
while True:
t = poll.poll(N * 1000)
if not t:
if not flg:
continue
print '\nresult => TIME OUT'
break
flg = True
c = sys.stdin.read(1)
sys.stdout.write(c)
sys.stdout.flush()
if c == '\n':
if S == s:
print 'result => OK'
else:
print 'result => NG'
break
s += c
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, attr)
poll.unregister(fd)
InputChecker(5, 'ABCDEF')
|
少しだけ改良。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | from sys import stdin, stdout
import tty, select
def write(s):
stdout.write(s)
stdout.flush()
def InputChecker(N, S):
write('input(%s) => ' % S)
poll = select.poll()
poll.register(stdin, select.POLLIN)
attr = tty.tcgetattr(stdin)
tty.setcbreak(stdin)
s = ''
try:
while True:
l = poll.poll(N * 1000)
if not l:
if not s:
continue
write('\nresult => TIME OUT\n')
return
c = stdin.read(1)
write(c)
if c == '\n':
write('result => %s\n' % ('OK' if S == s else 'NG'))
return
s += c
finally:
tty.tcsetattr(stdin, tty.TCSADRAIN, attr)
poll.unregister(stdin)
InputChecker(5, 'ABCDEF')
|
条件6の対応もれを訂正。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | from sys import stdin, stdout
import tty, select
def write(s):
stdout.write(s)
stdout.flush()
def InputChecker(N, S):
write('input(%s) => ' % S)
poll = select.poll()
poll.register(stdin, select.POLLIN)
attr = tty.tcgetattr(stdin)
tty.setcbreak(stdin)
s = ''
timeout = False
try:
while True:
if not poll.poll(N * 1000):
if not s:
continue
timeout = True
c = stdin.read(1)
write(c)
if c == '\n':
write('result => %s\n' % ('TIME OUT' if timeout else 'OK' if S == s else 'NG'))
return
s += c
finally:
tty.tcsetattr(stdin, tty.TCSADRAIN, attr)
poll.unregister(stdin)
InputChecker(5, 'ABCDEF')
|
># try節からreturnしてもfinally通るんだっけかな。 通ります。 >>> def foo(): try: return finally: print 1 >>> foo() 1
c-wrapperでcursesを使いました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | (use c-wrapper)
(c-load "curses.h" :libs "-lcurses")
(initscr)
(define (input-checker n str)
(cbreak)
(printw "input(%s) => " str)
(refresh)
(receive (t input) (let* ((c0 (integer->char (getch)))
(st (sys-time)))
(do ((c c0 (integer->char (getch)))
(lst '() (cons c lst)))
((memq c '(#\lf #\cr))
(values (- (sys-time) st) (list->string (reverse lst))))))
(printw "result => %s\n" (cond
((< n t) "TIME OUT")
((equal? str input) "OK")
(else "NG")))
(refresh)))
(input-checker 5 "ABCDEF")
|
すみません、さっきのコードでmemqを使っている箇所がありますが、文字の判定なので正しくはmemvを使うべきですね。 Gauche 0.8.11だとmemqでも動くようですが。
手元で試してみたら最後に(endwin)の 終了処理を入れないと、 モードが元にもどらないでエスケープ絡みがおかしくなるようです。 しかしc-wrapperってすごいですね。
たしかにmanで見てみると、"A program should always call endwin before exiting or escaping from curses mode temporarily." とあるので、endwinを呼ぶべきですね。行儀が悪いプログラムでした。
Emacs のミニバッファから。
1 2 3 4 5 6 7 | (defun input-checker (n s)
(interactive "nTime(second): \nsString: ")
(let ((c (read-char (concat "input(" s ")=>"))))
(push c unread-command-events))
(with-timeout (n (message "TIME OUT"))
(let ((input (read-string (concat "input(" s ")=>"))))
(message (if (string= input s) "OK" "NG")))))
|
秒数はあまり正確でないです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using System;
using System.Threading;
class Program {
static void InputCheck(int n, string s) {
Console.Write("input({0}) => ", s);
while (!Console.KeyAvailable) Thread.Sleep(100);
DateTime timeout = DateTime.Now.AddSeconds(n);
string match = s.Equals(Console.ReadLine()) ? "OK" : "NG";
Console.WriteLine("result => {0}", DateTime.Now < timeout ? match : "TIME OUT");
}
static void Main(string[] args) {
InputCheck(5, "ABCDEFG");
InputCheck(4, "HIJKLMN");
InputCheck(3, "OPQRSTUVWXYZ");
}
}
|
ReadLine()は使わないで書いてあるようなのですが、 平気なのでしょうか? by C#わからない人
see: KeyAvailable プロパティは、Read メソッドや ReadLine メソッドではなく、必ず、ReadKey メソッドとの組み合わせで使用してください。
KeyAvailableがtrueでも、ReadLine, Readは、Enterキー待ちになるので組み合わせるな、と解釈しましたが…
他の理由で組み合わせるなと書いてる可能性も、無いとはいえないですね。
ReadKeyを使ったのも書いて比べてみました。
動作の違いは、ReadLineは整形済みの文字列の入力であるのに対し、ReadKeyは、コード単位の入力になります。
具体的には、バックスペースが'\b'として入力される、日本語入力には、対応するエンコーディングの変換が必要、など。
なので、このコードはASCIIコードのみ対応で、打ち間違いは許されません。
他の理由で組み合わせるなと書いてる可能性も、無いとはいえないですね。
ReadKeyを使ったのも書いて比べてみました。
動作の違いは、ReadLineは整形済みの文字列の入力であるのに対し、ReadKeyは、コード単位の入力になります。
具体的には、バックスペースが'\b'として入力される、日本語入力には、対応するエンコーディングの変換が必要、など。
なので、このコードはASCIIコードのみ対応で、打ち間違いは許されません。
1 2 3 4 5 6 7 8 9 10 11 12 13 | static void InputChecker(int n, string s) {
StringBuilder sb = new StringBuilder(s.Length * 2);
Console.Write("input({0}) => ", s);
ConsoleKeyInfo key = Console.ReadKey();
DateTime timeout = DateTime.Now.AddSeconds(n);
while (key.Key != ConsoleKey.Enter) {
sb.Append(key.KeyChar);
key = Console.ReadKey();
}
string match = s.Equals(sb.ToString()) ? "OK" : "NG";
Console.WriteLine();
Console.WriteLine("result => {0}", DateTime.Now < timeout ? match : "TIME OUT");
}
|
難しいですね……。なんでダメなんだろ。少し調べてみました。 ReadKeyとKeyAvailableは、それぞれWindows APIのReadConsoleInputとPeekConsoleInputを内部で呼び出しています。 これらの関数の引数にはコンソール入力バッファのハンドルを必要としていて そのハンドルをWindows APIのGetStdHandleで取得し使用しています。 ReadKeyとKeyAvailableではこのハンドルを共有して(Private変数で)いるのに対し ReadLineではメソッドが呼ばれたときGetStdHandleを呼び出し新たにハンドルを取得して それを元にSystem.IO.Streamオブジェクトを作成しています。 ハンドルの値は結果的に同じになるのでしょうが、ここらあたりが原因なのかな? そしてVB.NETのコードも併せて。ReadLineの方で……。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Sub Main()
InputCheck(5, "ABCDEFG")
InputCheck(4, "HIJKLMN")
InputCheck(3, "OPQRSTUVWXYZ")
End Sub
Sub InputCheck(ByVal n As Integer, ByVal s As String)
Console.Write("input({0}) => ", s)
Do While Not Console.KeyAvailable
Threading.Thread.Sleep(100)
Loop
Dim time As DateTime = Now
Dim input As String = Console.ReadLine
Dim a As Double = Now.Subtract(time).TotalMilliseconds
If Now.Subtract(time).TotalMilliseconds > 1000 * n Then
Console.WriteLine("TIMEOUT")
ElseIf input.Equals(s) Then
Console.WriteLine("OK")
Else
Console.WriteLine("NG")
End If
End Sub
|
Windows専用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | #include <windows.h>
#include <stdexcept>
#include <string>
char GetChar(HANDLE h)
{
char c;
DWORD read;
while (true)
{
if (! ::ReadFile(h, &c, 1, &read, NULL))
{
throw std::runtime_error("::ReadFile() failed");
}
if (read == 1 && c != '\r') // skip CR
{
break;
}
}
return c;
}
void WriteString(HANDLE h, const std::string& s)
{
const char* p = s.c_str();
DWORD size = s.size();
while (size)
{
DWORD written;
if (! ::WriteFile(h, p, size, &written, NULL))
{
throw std::runtime_error("::WriteFile() failed");
}
p += written;
size -= written;
}
::FlushFileBuffers(h);
}
void InputChecker(DWORD timeout, const std::string& expected)
{
HANDLE hStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
HANDLE hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdIn == INVALID_HANDLE_VALUE || hStdOut == INVALID_HANDLE_VALUE)
{
throw std::runtime_error("cannot get console handle");
}
WriteString(hStdOut, "input(" + expected + ") => ");
::WaitForSingleObject(hStdIn, INFINITE);
const DWORD beg = ::GetTickCount();
std::string s;
char c;
while ((c = GetChar(hStdIn)) != '\n')
{
s.append(1, c);
}
const DWORD end = ::GetTickCount();
std::string result;
if (end - beg >= timeout * 1000)
{
result = "TIME OUT";
}
else if (s == expected)
{
result = "OK";
}
else
{
result = "NG";
}
WriteString(hStdOut, " result => " + result + "\r\n");
}
int main()
{
try
{
while (true)
{
InputChecker(5, "ABCDEF");
}
}
catch (std::exception& e)
{
WriteString(::GetStdHandle(STD_ERROR_HANDLE), std::string("error: ") + e.what() + "\r\n");
}
}
|
3回実行というのを見落としてました。
1 2 3 4 5 6 7 8 9 10 11 | --- orig.cpp Wed Oct 3 04:14:39 2007
+++ main.cpp Wed Oct 3 04:15:00 2007
@@ -97,7 +97,7 @@
{
try
{
- while (true)
+ for (int i = 0; i < 3; ++i)
{
InputChecker(5, "ABCDEF");
}
|
真っ先に投稿があると思っていたCがなかなかこないので投稿(笑 環境がcygwinのせいかcursesのライブラリがうまく動かず、 それならばと int 16hしてみたがやっぱりダメ;; ということで、termiosを使いました。 あといらんコード混ぜて見た目ちょっとだけかっこよくしてみた(笑 //gcc -Wall -std=c99 doukaku64.c -o test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/syslimits.h>
#include <unistd.h>
#include <termios.h>
int InputChecker(time_t sec, const char *str)
{
size_t length = strlen(str); // オリジナルの文字数
char in[ LINE_MAX+1 ]; // 入力文字列
size_t len; // 入力文字数
time_t t; // 入力時間
struct termios tos, tos_org;
tcgetattr( 0, &tos ); tos_org = tos;
memset(in, '\0', sizeof(in));
printf("input:[%s] => 入力待ち\r", str); fflush(stdout);
printf("input:[%s] => ", str); |





raynstard
#3420()
Rating0/2=0.00
関数 InputCheckerは、以下の仕様を満たしてください。
たとえば、「InputCheker(5, "ABCDEF")」と指定した場合、 出力例はこんな感じです。
1. input(ABCDEF) =>と出力して入力待ちをし、ユーザーが「ABCDEF<ENTER>」を入力したとき、 入力開始から5秒以内ならば「OK」、5秒をこえていれば「TIME OUT」を出力します。 このとき、ユーザーがキーを押下しなければ1. を出力してから たとえ10秒たっていても「TIME OUT」にはならないので注意してください。 時間計測はあくまでユーザーが入力を開始してからです。
このお題はraynstardさんの投稿です。ご協力ありがとうございます。
[ reply ]