相信被头歌的禁止复制粘贴恶心到了的人肯定不止我一个,这次写一个链表的题,每一关都要用到上一关的代码,每一关都要重新把4种构造函数和4个功能函数重新打一遍,只能说很符合我对头歌的印象😅😅😅。
于是本着偷懒顺便造福大众的想法,我开始寻找解决的办法。经过一天一夜的尝试与搜索,终于找到了一种解决办法:调用系统API模拟键盘输入。

UPD:添加了一种新的方法,使用SendInput函数,可以输入中文。

正文

如何调用系统API呢?如果是大佬当然有无数的方法,但我们作为小白,选择最简单的方法就好了,那就是我们无敌的Python(撒花)

首先,安装PyWinHookPyUserInput
PyWinHook是Python的一个第三方库,可以对键盘、鼠标进行监听,PyUserInput可以模拟键盘鼠标输入。
https://www.lfd.uci.edu/~gohlke/pythonlibs/#pywinhook
在网址中找到对应的版本下载,如我的Python版本是3.10.10 64bit,那么就下载pyWinhook‑1.6.2‑cp310‑cp310‑win_amd64.whlcp310表示3.10amd64表示64位。
Python版本可以在命令行输入python查看,或者如果用的vscode可以直接在任意Python文件界面的右下角看到。

下载完毕后,打开文件夹,右键刚下载的文件,复制文件路径。然后在命令行中输入

1
2
pip install 路径
pip install PyUserInput

这样,我们就安装好了需要的第三方库,可以开始愉快地偷懒了ヾ(≧▽≦*)o



本来应该是这样的……但实际用了之后,我才发现这玩意跑起来跟我想的根本不一样。因为遇到了一大障碍——代码补全。
本来方便敲代码地代码补全,在这个时候却起了反作用。由于编辑器自动补全括号,自动缩进,就导致原本正常的代码打出来之后就变得,嗯……一言难尽。只能说,作为一名程序员,在看到这样的代码时,本能地产生了一种恐惧。
然后,我想着只要先对字符串进行一下修改,比如把会自动补全的}之类的去掉,删去部分回车和空格等。再然后,就陷入了长达一天的修改与尝试的循环(一天一夜里这个占了一天)……头歌的代码补全绝对是我见过的最魔幻的东西,我打一个}它就再给我补一个,我不打}它也不补,我打一个回车能变成两个回车,一个制表变成两个制表,零个制表还是零个制表……一天下来,我仿佛见证了生命进化的万千变化,整个人的精神都达到了新的高度~~(血压)~~。

那么说回正题,经过一天的尝试,最终找到了一种稍微正常的处理方法,能让打出来的代码没有那么抽象,以下是代码:

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
from pykeyboard import *
from pymouse import *
import time

m = PyMouse() # 建立鼠标对象
k = PyKeyboard() # 建立键盘对象

time.sleep(3) # 延迟几秒以移动鼠标

str_in = '''#include "ArrayList.h"
ArrayList::ArrayList()
:data{new int[1010]}, capacity{0}, List(0){
std::cout << size << std::endl;
}
ArrayList::ArrayList()
:data{new int[1010]}, capacity{0}, List(0){
std::cout << size << std::endl;
for (int i = 0; i < n; ++i) {
cout << fuck;
}
}
'''# 粘贴代码,最好与示例格式一致
str_in = str_in.replace(' ', '')
str_in = str_in.replace('\n}', '}\n') # 玄学操作
k.type_string(str_in)

这样处理后,效果差不多是这样res
相信我,这已经是最正常的一个版本了,没有超出屏幕的缩进,没有多出来的空行,没有多出来的大括号,相比于其他版本,简直就是奈亚子与奈亚拉托提普相比。

当然,如果有人有足够的耐心、时间,也可以寻找一下更优的办法,找到了记得踢我一脚,反正我是不想尝试了……

花絮

err(失败尝试……)


经过这次事件我才发现,头歌的格式化代码功能会把头文件引入的#include格式化到宏定义#define的后面,还会把->符号变成- >导致编译错误……本来以为把n代码补全为using namespace已经够逆天了,没想到还有高手……


UPD:在做别的东西的时候发现了一个新的Windows API——SendInput,这个函数向窗口发送事件,也可以模拟输入,更重要的是,这个函数是可以输入中文的,但是好像不能输入回车,因此诞生了一种新的偷懒方法,适用于不同场合。
代码直接抄的StackOverFlow,把字符串改改就能用,需要注意的是,这种方法不能输入回车,也就是说你的代码会集中在一行,但好处就是不会因为回车和补全多个大括号之类的,对于C++来说,只需要输入后在#include等预处理指令后加个回车就能跑,就是看起来完全不像人类写的,如果你能够保证老师不会事后检查代码,同时使用的语言不需要回车就能解析(这个方法使用了Python,却不能输入Python……),也可以尝试这种方法。

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
import ctypes as ct
import ctypes.wintypes as w
import struct
import time
import array

KEYEVENTF_SCANCODE = 0x8
KEYEVENTF_UNICODE = 0x4
KEYEVENTF_KEYUP = 0x2
SPACE = 0x39
INPUT_KEYBOARD = 1

# not defined by wintypes
ULONG_PTR = ct.c_size_t

class KEYBDINPUT(ct.Structure):
_fields_ = [('wVk' , w.WORD),
('wScan', w.WORD),
('dwFlags', w.DWORD),
('time', w.DWORD),
('dwExtraInfo', ULONG_PTR)]

class MOUSEINPUT(ct.Structure):
_fields_ = [('dx' , w.LONG),
('dy', w.LONG),
('mouseData', w.DWORD),
('dwFlags', w.DWORD),
('time', w.DWORD),
('dwExtraInfo', ULONG_PTR)]

class HARDWAREINPUT(ct.Structure):
_fields_ = [('uMsg' , w.DWORD),
('wParamL', w.WORD),
('wParamH', w.WORD)]

class DUMMYUNIONNAME(ct.Union):
_fields_ = [('mi', MOUSEINPUT),
('ki', KEYBDINPUT),
('hi', HARDWAREINPUT)]

class INPUT(ct.Structure):
_anonymous_ = ['u']
_fields_ = [('type', w.DWORD),
('u', DUMMYUNIONNAME)]

print(ct.sizeof(INPUT))

def zerocheck(result, func, args):
if result == 0:
raise ct.WinError(ct.get_last_error())
return result

user32 = ct.WinDLL('user32', use_last_error=True)
SendInput = user32.SendInput
SendInput.argtypes = w.UINT, ct.POINTER(INPUT), ct.c_int
SendInput.restype = w.UINT
SendInput.errcheck = zerocheck

def send_scancode(code):
i = INPUT()
i.type = INPUT_KEYBOARD
i.ki = KEYBDINPUT(0, code, KEYEVENTF_SCANCODE, 0, 0)
SendInput(1, ct.byref(i), ct.sizeof(INPUT))
i.ki.dwFlags |= KEYEVENTF_KEYUP
SendInput(1, ct.byref(i), ct.sizeof(INPUT))

def send_unicode(s):
i = INPUT()
i.type = INPUT_KEYBOARD
# Handles non-BMP code points by sending UTF-16 code units.
for c in array.array('H', s.encode('utf-16le')):
i.ki = KEYBDINPUT(0, c, KEYEVENTF_UNICODE, 0, 0)
SendInput(1, ct.byref(i), ct.sizeof(INPUT))
i.ki.dwFlags |= KEYEVENTF_KEYUP
SendInput(1, ct.byref(i), ct.sizeof(INPUT))

# Allow time to focus on another app
# such as Notepad or Notepad++
time.sleep(3)

send_scancode(SPACE)

# Last character is 7 code points:
# U+1F468 MAN
# U+200D ZERO WIDTH JOINER
# U+1F469 WOMAN
# U+200D ZERO WIDTH JOINER
# U+1F467 GIRL
# U+200D ZERO WIDTH JOINER
# U+1F466 BOY

code = """
"""

send_unicode(code)