暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

用Python自动批改C语言作业(二)

语和言 2018-07-07
532

一、任务


上次的《用Python自动批改C语言作业(一)》提到对于末尾有getch();语句的C程序,Python未能成功运行它并捕获它的运行结果。为此,小编又做了今天的实验。


二、环境


外甥打灯笼——照旧(舅)。

Win7 32位 + Python3.64 + GCC编译器


三、程序


由于上次提到的四个程序当中,不带getch();语句的我们已经搞定了,剩下两个带getch();语句的程序只是编码的不同,而编码的不同我们可以通过设置不同的编译参数来解决。所以,我们只需搞定ANSI编码的带getch();语句的C程序的自动编译和运行问题就行了。这里给出一个简单的C程序文件test.c,代码如下:


#include <stdio.h>

#include <conio.h>

int main(int argc, char *argv[])

{

printf("姓名:张三\n年龄:23\n");

getch();

return 0;

}


四、思路


经过查阅互联网上众多的帖子,得到的结论是Python在执行外部exe文件的时候,os.popen功能有限,只能捕获exe的输出,但不能将exe所需的键盘输入数据喂给它。而getch();语句正是需要一个从键盘输入的任何字符,所以就没有办法了。很多人推荐用subprocess.Popen来代替os.popen。查了关于subprocess.Popen的一些资料,将代码

    fileobj = os.popen("%s a"% exefn)

    content = fileobj.read()

    fileobj.close()

    print(content)

修改为

    p = subprocess.Popen(exefn, stdin = subprocess.PIPE,

                         stdout=subprocess.PIPE)

    datasource = "a\n"

    result = p.communicate(input = datasource.encode("gbk"))

    content = result[0].decode("gbk")

    print(content)

其它不变。为方便查看,这里给出全部代码。




五、运行


程序运行的时候,首先在Python输出窗口中输出了一些文字,然后出现一个黑黑的命令行窗口。如下图所示:



此时按一下任何一个按键,命令行窗口消失,Python输出窗口中多了一些文字,全部结果如下:


D:\ftp\c\test.c

文件编码:GB2312

编译输出文件名构造:D:\ftp\c\a.exe

编译字符串:gcc "D:\ftp\c\test.c" -g -finput-charset=GB2312 -fexec-charset=GBK -o "D:\ftp\c\a.exe"

程序输出结果:

姓名:张三

年龄:23


我们发现,这次修改代码,虽然也阻塞了程序的自动运行,但至少能出现黑窗口等着按一下键,不至于让我们请出杀进程的法宝。更重要的是,杀进程出不来任何结果,这里按一下任意键就能得到C程序的输出结果。这个效果我们还是较为满意的。美中不足的是,黑屏还在,并没有自动结束。


六、改进


按照subprocess.Popen的资料,我们用p.stdin.write(datasource)就可以为C程序提供所需要的输入数据,这里的C程序不就是需要我们按任意键吗?那我给提供了两个字符,它居然还接收不到数据?将"a\n"改为"a"或者"\n"或者"/n",均有黑屏出现等着按一下键。难道getch();语句比较顽固,油盐不进?

为此,改进了一下代码:

    datasource = "a\n".encode("gbk")

改为

    datasource = "张三丰\n100".encode("gbk")


同时,增加一个ANSI编码的C程序test2.c,内容如下:


#include <stdio.h>

#include <conio.h>

int main(int argc, char *argv[])

{

char name[100];

int age;

gets(name);

scanf("%d", &age);

printf("姓名:%s\n年龄:%d\n", name, age);

getch();

return 0;

}


然后,开始运行Python程序。照例要按一下键关掉过黑屏窗口,运行结果如下:


D:\ftp\c\test.c

文件编码:GB2312

编译输出文件名构造:D:\ftp\c\a.exe

编译字符串:gcc "D:\ftp\c\test.c" -g -finput-charset=GB2312 -fexec-charset=GBK -o "D:\ftp\c\a.exe"

程序输出结果:

姓名:张三

年龄:23


D:\ftp\c\test2.c

文件编码:ISO-8859-9

编译输出文件名构造:D:\ftp\c\a.exe

编译字符串:gcc "D:\ftp\c\test2.c" -g -finput-charset=ISO-8859-9 -fexec-charset=GBK -o "D:\ftp\c\a.exe"

编译失败,没有生成文件:D:\ftp\c\a.exe


第二个程序test2.c居然编译失败。原因是其编码被错误地识别成了ISO-8859-9,查资料发现这是土耳其语的编码。判断编码的函数getthe_encoding_of_txt_file用的是chardet扩展包:


def getthe_encoding_of_txt_file(fn):

    from chardet import detect

    with open(fn, "rb+") as fp:

        content = fp.read()

        encoding = detect(content)['encoding']

        return encoding


它也有判断出错的时候。这不由让人想起“联通”两个字用记事本保存成ANSI编码再打开的惊喜变脸效果。既然这儿出了问题,我们得考虑修改这个函数本身或者修改该函数的返回值。咱们中国人编写的C程序,要么是AMNSI/GB2312/GBK系列编码,要么是UTF-8编码。我们可以考虑如果不是UTF-8编码的C程序,都通通按GBK编码来编译。因此,保留函数getthe_encoding_of_txt_file

不变,等收到返回值之后再修改这个值,确保是"UTF-8"和"GBK"之一。为此增加如下代码:


    if encoding.lower() != "utf-8":

        encoding = "gbk"


再次运行Python代码,按两次键忽略黑屏,此时的输出结果如下:


D:\ftp\c\test.c

文件编码:GB2312

编译输出文件名构造:D:\ftp\c\a.exe

编译字符串:gcc "D:\ftp\c\test.c" -g -finput-charset=gbk -fexec-charset=GBK -o "D:\ftp\c\a.exe"

程序输出结果:

姓名:张三

年龄:23


D:\ftp\c\test2.c

文件编码:ISO-8859-9

编译输出文件名构造:D:\ftp\c\a.exe

编译字符串:gcc "D:\ftp\c\test2.c" -g -finput-charset=gbk -fexec-charset=GBK -o "D:\ftp\c\a.exe"

程序输出结果:

姓名:张三丰

年龄:100


此时,我们发现,test2.c需要的两个数据(姓名和年龄)从Python程序传入成功。

可见,我们用Python的subprocess.Popen运行外部exe文件并为其传递数据的做法是没有问题的。有问题的只是getch();而已,它油盐不进,我们能有什么办法?

突然想到,以前在慕课网参加C语言学习的时候,凡是提交的作业,不许有提示性的输出,末尾不许用getch();语句,好像明白点什么了。


七、讨论


惹不起,俺躲还不行吗?

因为我们是自动批改作业,就无需让用户看看屏幕上输出的什么东西再按一下键结束黑屏了,凡是学生提交的C程序里面有getch();语句的地方,我们都给他换成return 0;语句。这样就不会出现黑屏等着按键进行干预了吧。我们捕捉到输出,再根据程序的具体要求去进一步处理就好了。

这个美妙主意,以后有时间再去尝试吧,俺得赶紧去让程序自动改C语言作业去了。

或许以后能有人找到搞定getch();语句的办法。


文章转载自语和言,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论