[ZOJ 3883] Scan Code 劳民伤财+劳神+并不科学的一个 模拟题

博客讲述了作者在解决ZOJ 3883模拟题时遇到的问题,包括扫描码的工作机制、特殊按键(Shift、Caps Lock、Backspace)在不同系统下的行为差异,以及比赛中如何处理这些细节。作者通过查找资料和实现代码,最终解决了问题,但在过程中发现题目给出的扫描码与实际PS/2键盘的码有所不同,需要进行调整。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先贴聊天记录一部分:

<zimpha#************> 2015-07-26 18:51:25
C题是我那个大四的final队友出的题....描述不清...坑人无数
<zimpha#************> 18:54:43 
现在内部题库里面已经没啥可用的题了..于是就挂出了这些恶心题

……说恶心,也没办法,硬着头皮做。

反正任务很简单,就是,翻译扫描码并做相应操作。

(扫描码的工作机制的话,就是每个按键有2个码,一个通码Make Code一个断码Break Code,按下的时候发一个通码,松开的时候发一个断码,具体机制可以参见《自己动手写操作系统》一书,自己用汇编+C语言写一个能处理键盘输入的,DOS味十足的底层程序,当然这不是这里的重点)

其他按键都还好了,只有4个特殊按键需要照顾:左右Shift、Caps Lock、Backspace。

然后他们怎么样的工作机制?!题目一句话都没说!!!!!!

题目居然忘了说明白这个看起来小但完全能影响结果的事情了!!!!!!

更糟糕的是,这些按键的组合行为在不同系统,不同编辑器下行为完全不同!

比如:

  • 大写锁关闭,按住CapsLock,然后打英文字母,Win下打出来的是大写字母,Mac下打出来的是小写字母!
  • 按住Shift,然后按Tab,Windows的记事本下还是会输出一个制表符( ' \t ' ),但是在Linux的gedit、Win的代码编辑器下,是撤销了缩进(删除一个制表符)!
  • 你按下Backspace的时候要我输出\b(ASCII 8,表Backspace)还是删去一个字符?

搞不清楚的细节问题太多了,想发 Clarification 问一下却不行,无力吐槽……


好吧,我们就按照Win下的行为写:

  • 按下CapsLock时,大写锁状态翻转,在按下期间CapsLock的状态不变(不管发了多少Make Code),直到松开后再次按下
  • 2个Shift互不干扰,CapsLock关闭的情况下,只要按下任何一个Shift,小写边大写,有2个字符的按键则打上面那个字符。两个Shift都按下的情况同理
  • CapsLock打开的情况下,按下任何一个Shift,按字母按键,输出小写字母,有2个字符的按键还是打上面那个字符。
  • 按住Shift和不按住Shift的时候按Tab,行为一致,输出一个 ' \t '(制表符)
  • 按下Backspace,收到一个Make Code就删除一个字符。

具体到实现的时候,主要就是,别偷懒,CapsLock除了需要标记是否打开,还要记得标记现在是否按下,按下了以后再发来Make Code就不要翻转CapsLock的状态了。

还有左右Shift,这两个Shift用2个独立变量标记是否按下。

还有请千万别没字符了还删,删除到负下标去了。


——诶呀,比赛没时间了,懒得打表怎么办?

——百度搜搜看啊,这种扫描码不可能一个个都造过去的

听从队友的建议,于是搜到了下面这两个

http://www.computer-engineering.org/ps2keyboard/scancodes2.html

http://www.computer-engineering.org/ps2keyboard/scancodes3.html

好像是现成的扫描码表格啊!

粘贴到Excel里,简单处理一下,搞到Notepad++里,然后整成代码的一部分,贴进代码里,最后补上那些特别的按键的行为控制,然后提交,WA……

……改了半天还是WA……

……赛后补题的时候发现,好像有的按键不对啊,题目里给的Make Code,和网上找的PS/2键盘实际的扫描码不一样啊!

于是折腾了1个小时,一个个对过去,终于改完,然后过了……

然后用Beyond Compare比对了一下前后的代码,发现出题人改了11个地方的MakeCode和相应的BreakCode……

 论出题人的心态与RP。 

最后贴一下代码

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;

map<int,char> smalldict,largedict;

char ans[5000005];
int ap;

char input[5000005];

void addChar(char x){
	ans[ap++]=x;
	ans[ap]=0;
}
bool caps;
bool Rshift;
bool Lshift;
bool capsDown;
int special_Control(int x){
	if(x==0x66){//backspace
		return 5;
	}else if(x==0x21){
		return ' ';
	}else if(x==0x0D){
		return '\t';
	}else if(x==0x58){//caps 2
		return 2;
	}else if(x==0x59){//Rshift 3
		return 3;
	}else if(x==0x12){//LShift 4
		return 4;
	}else if(x==0x5A){
		return '\n';
	}else return 0;
}

int char2int(char a,char b){
	int x1,x2;
	if(isdigit(a)) x1=a-'0';
	else x1=a-'A'+10;
	if(isdigit(b)) x2=b-'0';
	else x2=b-'A'+10;
	return x1*16+x2;
}

int main(){
	smalldict[0x1C]='a';
	smalldict[0x32]='b';
	smalldict[0x29]='c';
	smalldict[0x25]='d';
	smalldict[0x24]='e';
	smalldict[0x2B]='f';
	smalldict[0x33]='g';
	smalldict[0x34]='h';
	smalldict[0x43]='i';
	smalldict[0x3B]='j';
	smalldict[0x42]='k';
	smalldict[0x4B]='l';
	smalldict[0x3A]='m';
	smalldict[0x31]='n';
	smalldict[0x44]='o';
	smalldict[0x4D]='p';
	smalldict[0x15]='q';
	smalldict[0x2C]='r';
	smalldict[0x1B]='s';
	smalldict[0x2D]='t';
	smalldict[0x3C]='u';
	smalldict[0x2A]='v';
	smalldict[0x1D]='w';
	smalldict[0x45]='x';
	smalldict[0x35]='y';
	smalldict[0x1A]='z';
	smalldict[0x22]='0';
	smalldict[0x16]='1';
	smalldict[0x1E]='2';
	smalldict[0x26]='3';
	smalldict[0x23]='4';
	smalldict[0x2E]='5';
	smalldict[0x36]='6';
	smalldict[0x3D]='7';
	smalldict[0x3E]='8';
	smalldict[0x46]='9';
	smalldict[0x0E]='`';
	smalldict[0x4E]='-';
	smalldict[0x55]='=';
	smalldict[0x5D]='\\';
	smalldict[0x54]='[';
	smalldict[0x5B]=']';
	smalldict[0x4C]=';';
	smalldict[0x52]='\'';
	smalldict[0x41]=',';
	smalldict[0x49]='.';
	smalldict[0x4A]='/';
	
	largedict[0x1C]='A';
	largedict[0x32]='B';
	largedict[0x29]='C';
	largedict[0x25]='D';
	largedict[0x24]='E';
	largedict[0x2B]='F';
	largedict[0x33]='G';
	largedict[0x34]='H';
	largedict[0x43]='I';
	largedict[0x3B]='J';
	largedict[0x42]='K';
	largedict[0x4B]='L';
	largedict[0x3A]='M';
	largedict[0x31]='N';
	largedict[0x44]='O';
	largedict[0x4D]='P';
	largedict[0x15]='Q';
	largedict[0x2C]='R';
	largedict[0x1B]='S';
	largedict[0x2D]='T';
	largedict[0x3C]='U';
	largedict[0x2A]='V';
	largedict[0x1D]='W';
	largedict[0x45]='X';
	largedict[0x35]='Y';
	largedict[0x1A]='Z';
	largedict[0x22]=')';
	largedict[0x16]='!';
	largedict[0x1E]='@';
	largedict[0x26]='#';
	largedict[0x23]='$';
	largedict[0x2E]='%';
	largedict[0x36]='^';
	largedict[0x3D]='&';
	largedict[0x3E]='*';
	largedict[0x46]='(';
	largedict[0x0E]='~';
	largedict[0x4E]='_';
	largedict[0x55]='+';
	largedict[0x5D]='|';
	largedict[0x54]='{';
	largedict[0x5B]='}';
	largedict[0x4C]=':';
	largedict[0x52]='"';
	largedict[0x41]='<';
	largedict[0x49]='>';
	largedict[0x4A]='?';
	
	
	while(~scanf("%s",input)){
		int len=strlen(input);
		Lshift=Rshift=caps=capsDown=false;
		ap=0;ans[0]=0;
		for(int i=0;i<len;i++) input[i]=toupper(input[i]);
		for(int i=0;i<len;i+=2){
			int code=char2int(input[i],input[i+1]);
			if(code==0xF0){
				int code2=char2int(input[i+2],input[i+3]);
				code2=special_Control(code2);
				if(code2==3) Rshift=false;
				else if(code2==4) Lshift=false;
				else if(code2==2) capsDown=false;
				i+=2;
			}else{
				int spid=special_Control(code);
				if(spid==3) Rshift=true;
				else if(spid==4) Lshift=true;
				else if(spid==2){
					if(!capsDown){
						caps=!caps;
						capsDown=true;
					}
				}
				else if(spid==5){
					if(ap) ap--;
					ans[ap]=0;
				}else if(spid){
					addChar(spid);
				}else{
					char c=smalldict[code];
					if(isalpha(c)){
						if(caps^(Lshift||Rshift)) c=largedict[code];
					}else{
						if(Lshift||Rshift) c=largedict[code];
					}
					if(c)addChar(c);
				}
			}
		}
		puts(ans);
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值