求老师帮忙实现这个主控台流程(二次输入验证的机制)
求老师帮忙实现这个主控台流程======================================
请设定节拍密码(printf)
'3' 键被按下
上下按键的时间间隔(节奏): 0.833s
'2' 键被按下
上下按键的时间间隔(节奏): 0.232s
'1' 键被按下
[Enter键被按下]
======================================
再输入一次节拍密码
'3' 键被按下
上下按键的时间间隔(节奏): 0.856s
'2' 键被按下
上下按键的时间间隔(节奏): 0.294s
'1' 键被按下
[Enter键被按下]
验证输入的密码为 3 2 1
按键的间隔时间(按键释放'放开'到下一个按键按下): 0.833s/0.856s 0.232s/0.294s
密码设定完成
输入"password"并按下回车键输入密码
=====================================
请输入密码
'3' 键被按下
上下按键的时间间隔(节奏): 0.843s
'2' 键被按下
上下按键的时间间隔(节奏): 0.239s
'1' 键被按下
[incorrect 判断] 密码错误且节奏误差超过原间隔时间5~10%
if P1(b)!=P1(a) or P2(b)!=P2(a)...
else if (R1 != (R1+30ms or R1-30ms)) or (R2 != (R2+30ms or R2-30ms))...
[correct 判断] 密码相同且节奏误差在5~10%
P123(a) = P123(b)
R12(a) = R12(b)
=============
程式码如下
程序代码:
#include <stdio.h> #include <windows.h> #define MAX_KEYS 50 #define MAX_INTERVALS (MAX_KEYS - 1) // 設定驗證密碼紀錄的間隔數比輸入按鍵少一 int main() { INPUT_RECORD input_record; HANDLE console = GetStdHandle(STD_INPUT_HANDLE); DWORD events_read; // DWORD變量: 事件讀取(記錄按鍵按下的時間和釋放的時間) LARGE_INTEGER start_time, end_time, frequency; BOOL last_event_was_release = FALSE; LARGE_INTEGER last_release_time; int key_count = 0; char keys[MAX_KEYS] = { 0 }; double intervals[MAX_INTERVALS - 1] = { 0 }; // 設定陣列紀錄間隔數組 QueryPerformanceFrequency(&frequency); printf("請設定節拍密碼\n\n"); while (1) { if (ReadConsoleInput(console, &input_record, 1, &events_read)) { if (input_record.EventType == KEY_EVENT) { if (input_record.Event.KeyEvent.bKeyDown) { if (last_event_was_release && input_record.Event.KeyEvent.wVirtualKeyCode != VK_RETURN) { LARGE_INTEGER press_time; QueryPerformanceCounter(&press_time); double interval = (double)(press_time.QuadPart - last_release_time.QuadPart) / frequency.QuadPart * 1.0; intervals[key_count - 1] = interval; // 將間隔時間紀錄 printf("上下按鍵的時間間隔(節奏): %.3fs\n", (double)(press_time.QuadPart - last_release_time.QuadPart) / frequency.QuadPart * 1.0); } QueryPerformanceCounter(&start_time); last_event_was_release = FALSE; if (key_count < MAX_KEYS) { keys[key_count++] = input_record.Event.KeyEvent.uChar.AsciiChar; } } else { LARGE_INTEGER release_time; QueryPerformanceCounter(&release_time); last_release_time = release_time; last_event_was_release = TRUE; if (input_record.Event.KeyEvent.uChar.AsciiChar != '\0') { printf("'%c' 鍵被按下\n", input_record.Event.KeyEvent.uChar.AsciiChar); } else { printf("Key pressed for %.3f s\n", (release_time.QuadPart - start_time.QuadPart) / frequency.QuadPart * 1.0); } } } } // 如果Enter鍵被按下,跳出設定密碼 if (GetAsyncKeyState(VK_RETURN) & 1) { printf("\n==============================================================================\n"); printf("\n 驗證輸入的密碼為 "); for (int i = 0; i < key_count; i++) { printf("%c ", keys[i]); } printf("\n"); // Output the intervals between key presses printf(" 按鍵的間隔時間(按鍵釋放'放開'到下一個按鍵按下): "); for (int i = 0; i < key_count - 2; i++) { printf("%.3fs ", intervals[i]); } printf("\n"); break; } } return 0; } /* 可以將程式更改為記錄每個按鍵釋放後到下一個按鍵按下時所花的時間,可以按照以下方式進行更改: 1. 宣告一個變數 last_release_time,用於存儲上一個按鍵釋放(放開)時的時間。 2. 將 first_event 變數更改為 last_event_was_release,表示上一個事件是否是按鍵釋放(放開)動作。 3. 如果 last_event_was_release 為 TRUE,則計算並print上一個按鍵釋放(放開)動作到當前動作的時間間隔。 4. 在事件處理代碼的最後,如果當前事件是按鍵釋放事件,則將 last_release_time 設置為當前事件的時間。 5. intervals,用存儲按下每個鍵之間的時間間隔 [備註] 若要加入鍵程時間可以在printf加入(double)(release_time.QuadPart - start_time.QuadPart) / frequency.QuadPart * 1.0),%3.f表示 [紀錄] QueryPerformanceCounter函數 : 獲取一個高精確住的計數器,可以用於紀錄發生在短時間的事件,並以毫秒(ms)紀錄。 在此程式中,其功能在於紀錄從目前按鍵釋放(放開)到下一個按鍵按下的間隔時間。 請設定節拍密碼(R以ms表示) P1(a) --R1=(P2dt-P1ut)ms P2(a) --R2=(P3dt-P2ut)ms P3(a) [判斷] if P1(b)!=P1(a) or P2(b)!=P2(a)... else if (R1 != (R1+30ms or R1-30ms)) or (R2 != (R2+30ms or R2-30ms))... */
参考程式码
程序代码:
#include <stdio.h> #include <math.h> #include <windows.h> void InputPassword( size_t maxcount, char keys[], double timepoints[] ) { LARGE_INTEGER frequency, start_time; QueryPerformanceFrequency( &frequency ); start_time.QuadPart = 0; bool keys_down[128] = { false }; // 紀錄 char 0-127 的按鍵狀態 HANDLE console = GetStdHandle(STD_INPUT_HANDLE); for( size_t i=0; i!=maxcount; ) { INPUT_RECORD input_record; DWORD events_read; if( !ReadConsoleInput(console,&input_record,1,&events_read) || events_read!=1 || input_record.EventType!=KEY_EVENT ) // 忽略非按鍵訊息 continue; bool bKeyDown = input_record.Event.KeyEvent.bKeyDown!=0; char AsciiChar = input_record.Event.KeyEvent.uChar.AsciiChar; if( bKeyDown && AsciiChar=='\r' ) // Enter鍵按下後不再紀錄 { keys[i++] = 0; break; } if( AsciiChar<' ' || AsciiChar>=127 ) // 忽略無法映射的字符(對應著locale("C")下的isprint) continue; AsciiChar = (AsciiChar>='a' && AsciiChar<'z') ? (AsciiChar-'a'+'A') : AsciiChar; // 忽略大小寫(否則要監看Shift鍵和Caps鍵,過於複雜) if( bKeyDown && !keys_down[AsciiChar] ) { LARGE_INTEGER timepoint; QueryPerformanceCounter( &timepoint ); if( i == 0 ) start_time = timepoint; keys_down[AsciiChar] = true; keys[i] = AsciiChar; timepoints[i++] = (timepoint.QuadPart-start_time.QuadPart)/(frequency.QuadPart+0.0); putchar( '*' ); } else if( !bKeyDown && keys_down[AsciiChar] ) { LARGE_INTEGER timepoint; QueryPerformanceCounter( &timepoint ); keys_down[AsciiChar] = false; keys[i] = AsciiChar; timepoints[i++] = (timepoint.QuadPart-start_time.QuadPart)/(frequency.QuadPart+0.0); } } putchar( '\n' ); } void ShowPassword( size_t maxcount, char keys[], double timepoints[] ) { printf( "{\n" ); bool keys_down[128] = { false }; for( size_t i=0; i!=maxcount && keys[i]!=0; ++i ) { keys_down[keys[i]] = !keys_down[keys[i]]; printf( " '%c' %-4s at %10.6fs\n", keys[i], (keys_down[keys[i]]?"Down":"Up"), timepoints[i] ); } printf( "}\n" ); } bool ShowDifference( size_t maxcount, char a_keys[], double a_timepoints[], char b_keys[], double b_timepoints[] ) { for( size_t i=0; i!=maxcount && a_keys[i]!=0; ++i ) { if( a_keys[i] != b_keys[i] ) { puts( "{ 按鍵順序不一致 }" ); return false; } } printf( "{\n" ); double a_keys_down[128] = { 0 }; double b_keys_down[128] = { 0 }; for( size_t i=0; i!=maxcount && a_keys[i]!=0; ++i ) { if( a_keys_down[a_keys[i]] == 0 ) { a_keys_down[a_keys[i]] = a_timepoints[i] + 1.0; b_keys_down[b_keys[i]] = b_timepoints[i] + 1.0; if( i == 0 ) printf( " '%c' Down:\n", a_keys[i] ); else printf( " '%c' Down: 按鍵時間相差 %10.6fs (%4.2f%%)\n", a_keys[i], fabs(a_timepoints[i]-b_timepoints[i]), fabs(a_timepoints[i]-b_timepoints[i])*100/a_timepoints[i] ); } else { double d1 = fabs( a_timepoints[i]+1.0 - a_keys_down[a_keys[i]] ); double d2 = fabs( b_timepoints[i]+1.0 - b_keys_down[b_keys[i]] ); printf( " '%c' Up : 按鍵時長相差 %10.6fs (%4.2f%%)\n", a_keys[i], fabs(d1-d2), fabs(d1-d2)*100/d1 ); a_keys_down[a_keys[i]] = 0; b_keys_down[b_keys[i]] = 0; } } printf( "}\n" ); puts( "如果覺得這個偏差可以接受,就返回true;否則,返回false" ); return true; } int main( void ) { char a_keys[50]; double a_intervals[50]; InputPassword( 50, a_keys, a_intervals ); ShowPassword( 50, a_keys, a_intervals ); char b_keys[50]; double b_intervals[50]; InputPassword( 50, b_keys, b_intervals ); ShowPassword( 50, b_keys, b_intervals ); ShowDifference( 50, a_keys, a_intervals, b_keys, b_intervals ); }