简介:SIMGUI 是一款基于 Electron 与 Element UI 开发的 C++/Python 代码查重工具,面向高校教师、研究者和开发团队,用于检测源代码相似度、辅助作业查重与代码质量管理。软件核心集成开源 SIM 算法,通过可交互界面完成代码导入、查重参数调整、相似区域比对和结果导出,使用者无需了解算法底层细节。压缩包共 87 个文件,约 70MB,主要包含 exe 主程序、dll 运行库、pak 界面与语言资源、png 操作示例图,以及 bin/json 等配置文件,解压后即可运行。依托 Electron,该工具支持 Windows、macOS、Linux 等常见系统。发布页已有 530 人学习下载,适合需要快速识别重复代码、规避抄袭风险并促进代码优化的场景。本地免安装运行还能减少对源码隐私的暴露,即使离线也可完成检测,报告会清晰列出相似代码块,便于逐段审查与后续重构。
1. SIMGUI 代码查重免安装版:解压即用,但先看懂它是怎么查的
期末交 C++ 大作业那几天,实验室里总有人在传一个绿色小工具:SIMGUI 代码查重免安装版。你把它解压到 U 盘里,双击就能用,把全班作业按学号放好,点两下鼠标就生成一份相似度报告——被怀疑互相抄袭的学号整整齐齐排在表头。它本质上是本地查重引擎 SIM(Software Integrity Measurement)的图形界面封装,sim_c 和 sim_py 分别负责 C/C++ 和 Python,所有比较都在本机完成,不联网、不上传代码。对助教、项目负责人、外包验收的人来说,这是最省事的一条路。但“免安装”三个字容易让人放松警惕:它省掉的是安装流程,不是运行时依赖,后面一半的坑都出在这里。这篇笔记就按“原理怎么跑、命令怎么敲、报告怎么读、参数怎么调、坑在哪”的顺序讲完。
2. 查重原理:token 流与公共子串匹配,C++ 和 Python 的差异在哪
2.1 从源码到 token 流:注释被丢掉,空白折叠,标识符保留
SIM 不直接比较“两行文本是否相同”,因为字符级比较太容易被伪装骗过去:你只要把整段代码换行、重新缩进,每一行都变了,行级比较就失灵了。所以 SIM 第一步是把源码喂给内置的词法分析器,切成 token 流。token 是源码的语义最小单元:关键字、标识符、数字、字符串、运算符各算一个 token。比如for (int i = 0; i < n; i++)会被切成一串for(inti=0;i<n;i++)。比较就在这串 token 上进行,而不是在原始文本上进行。
token 化过程中有三个归一化动作直接决定它能抓什么:第一,空白和换行被折叠,所以“把代码重排缩进”在这个工具面前基本无效;第二,注释默认被跳过,所以“删掉注释再交”也不会降低相似度;第三,标识符保留原名。这一点和很多人的直觉不一样:SIM 不会像某些在线查重系统那样把所有变量名抽象成同一个记号,所以“把 a 改成 b、把函数名整体换一遍”这种操作确实能降低 SIM 的分数。这也是为什么 SIM 的报告不能直接当判罚依据——它筛出来的东西,需要人工再扫一遍。你可以把这理解为黑匣子的一个已知盲区:机械改名能骗过它,但语义等价重写更能骗过它。
2.2 公共子串匹配:最小匹配长度决定误报与漏报
拿到 token 流之后,SIM 对每两个文件做一次两两比较,寻找足够长的公共 token 子串。这个“足够长”就是参数-r的物理意义:连续多少个 token 完全一致,才算一次有效匹配。-r取太小,for (int i = 0;这种任何两个写 C++ 的人都会撞车的三五个 token 也会被算进去,全班相似度轻松破 40%,报告失去区分度;-r取太大,故意穿插修改的长代码块又匹配不上,漏报严重。每个匹配片段都带坐标(在哪个文件第几行到第几行),所以报告绝不是孤零零一个百分比,而是“哪些地方像,像到什么程度”。
相似度的计算口径是:文件里所有被匹配的 token 数占这个文件总 token 数的比例。因为两份文件长度不一定相同,所以一对文件会有两个百分比,通常不对称。还要记住一点:SIM 是切片级相似检测,不是语义查重,它抓不到“按语义完全重写”的代码。这是工具边界,不是版本缺陷。实操里有一个很常见的结构值得注意:如果 A 和 B 都抄了同一份源文件 C,报告往往出现 A-C 很高、B-C 也很高,但 A-B 反而低的现象——因为 A 和 B 各自改了不同的变量名和插入位置。只看最高分对,会漏掉这种星型抄袭结构,正确做法是把所有超过阈值的对全部扫一遍。
2.3 C++ 与 Python 的 token 化差异:参数默认值为什么不一样
C++ 的 token 流里符号 token 特别多:分号、括号、花括号、双冒号、模板尖括号到处都是,预处理指令#include还会把一整行公共内容变成 token 参与比较。这带来的直接后果是:C++ 查重必须先处理公共头文件,否则每个人#include <iostream>和 using 声明本身就贡献了大量公共 token,基线相似度被无谓抬高。我一般会建一个common.txt排除清单,把课程提供的模板头文件、公共框架代码全部列进去,再做正式比对。
Python 的情况相反:关键字密度高、符号 token 少、没有预处理器,缩进被折叠之后,代码块结构主要通过defclassiffor这些关键字体现。相同逻辑的两段 Python,切出来的 token 序列更像自然语言的句子,匹配片段往往更长、更完整,所以-r可以比 C++ 取更大。我的经验值是:C/C++ 用-r 5左右,Python 用-r 10左右。这两个不是官方默认值,是根据 token 流密度调出来的经验值,后面讲调参时会说明怎么根据报告反推。免安装包不会替你区分这些差异——GUI 让你选语言,本质就是帮你选调用哪个核心程序。
2.4 免安装版的内部构成:GUI 前端只是传话人
社区里叫 SIMGUI 的封装版本很多,但结构大同小异,本质都是“图形前端 + 命令行核心”。你双击的 GUI 不实现任何查重算法,它只做三件事:收集参数、调用核心 exe、渲染报告。
SIMGUI/ ├─ SIMGUI.exe # 图形前端,常见为 .NET/WinForms 或 Delphi 编写 ├─ sim_c.exe # C/C++ 查重核心 ├─ sim_py.exe # Python 查重核心 ├─ sim_java.exe # Java 查重核心 ├─ sim_text.exe # 纯文本查重核心 ├─ config.ini # 前端持久化:语言、阈值、运行长度 └─ report/ # 报告输出目录,部分版本直接在界面里展示判断一个免安装版是否完整,第一件事就是核对这份核心程序清单。很多人下载的版本只带了 GUI 和 sim_c.exe,切到 Python 查重时界面毫无反应——那是因为 sim_py.exe 压根不存在。免安装的真正价值是:不写注册表、不装系统服务、删掉目录就是卸载,适合教学机房和 U 盘场景。但要注意,GUI 和核心程序大多是用 MSVC 或 .NET 编译的,运行仍依赖 Microsoft Visual C++ 运行库,最常见的是 2015-2022 Redistributable (x64)。Win10/11 多半自带或能快速补装,老系统则经常在这里翻车,这一条到第 5 章避坑部分展开。
3. 免安装版实操:用 sim_c / sim_py 跑通第一次代码查重
3.1 拿到免安装版后先确认四件事
无论从哪个渠道拿到的包,先别急着双击 GUI。我一般会做四步确认,能省掉后面两个小时的问题排查。
第一,核对 2.4 里的目录清单,确认你要用的语言核心 exe 在不在。第二,在免安装版目录里开一个 cmd 窗口,直接执行sim_c.exe -h,能打印一屏英文帮助说明运行库正常;如果报“缺少 VCRUNTIME140.dll”或“找不到 MSVCP140.dll”,说明机器缺 VC++ 运行库,先去装 Microsoft Visual C++ 2015-2022 Redistributable (x64)。第三,把所有路径改成纯英文,GUI 和核心程序经常按 ANSI 编码解析参数,中文路径会产生大量“找不到文件”的玄学问题。第四,源文件统一保存为 UTF-8 无 BOM,C++ 源文件里如果有 GBK 编码的中文注释,建议先转码再跑,否则可能出现“文件读不进去但程序不报错”的静默跳过。
这里顺便回一个高频误区:很多人以为跑 sim_py 需要先装 Python 解释器,甚至要先配置 vscode 的 C/C++ 环境——完全不需要。sim_py.exe 是编译好的独立程序,它对 Python 源码做词法分析是在自己进程里完成的,你机器上有没有 Python 都不影响。需要 Python 的场景只有一个:你想写批处理脚本批量调用这些 exe,那是另一回事,第 6 章会讲。
3.2 第一个查重命令:C++ 全目录跑通
确认完四件事后,用命令行跑通一次是最稳妥的。假设所有待查的.c/.cpp文件都在D:\submissions下面,公共模板清单是common.txt:
cd /d C:\CheckTool sim_c.exe -t 30 -r 5 -e common.txt D:\submissions\*.c D:\submissions\*.cpp > report_c.txt这条命令做了四件事:-t 30只显示相似度大于等于 30% 的文件对,避免输出文件大到没法翻;-r 5表示 5 个连续 token 以上的匹配才算数,对应 C++ 场景的常用最小匹配长度;-e common.txt把公共模板从头文件集合里排除;> report_c.txt把结果落盘。跑完之后打开 report_c.txt,第一屏应该是“Comparing: 文件名列表”,往下是每一对文件的百分比和匹配片段坐标。
一个容易翻车的细节:cmd 命令里的D:\submissions\*.c是否生效,取决于 sim_c.exe 自己有没有内置通配符展开逻辑。老版本工具经常没有,结果是把*.c当成一个普通文件名处理,然后报“无法打开文件”。遇到这种情况不要硬磕 cmd,换 PowerShell 把文件列表先展开成数组再传给命令:
$files = Get-ChildItem -Path D:\submissions -Include *.c,*.cpp -Recurse | Select-Object -ExpandProperty FullName & .\sim_c.exe -t 30 -r 5 -e common.txt $files | Out-File report_c.txt -Encoding utf8这里Get-ChildItem -Recurse顺带解决了另一个常见的 GUI 短板——很多免安装版的图形界面不支持递归子目录,只扫当前文件夹;而命令行配合 PowerShell 可以一次性把子目录里的文件全部卷进来。PowerShell 的数组传参也是真实可用的,$files会被自动展开成多个命令行参数。
Python 的首次运行同理,只需换核心程序和-r取值:
sim_py.exe -t 30 -r 10 -e common.txt D:\submissions\*.py > report_py.txtPython 的-r 10依据在 2.3 里说过:它的 token 流更稀疏,公共语法片段更短,-r太小会误报泛滥。跑完先不要调任何参数,直接看报告。
3.3 必调参数:-r 最小匹配长度、-t 阈值与 -e 排除清单
参数 作用 常见取值 -r N 连续 N 个 token 完全一致才算匹配 C/C++:3~8;Python:8~15 -t N 只显示相似度 >= N% 的文件对 初筛 20~30;坐实 60 以上 -e FILE 排除清单,每行一个文件名 公共头文件、课程模板、框架代码三个参数里最容易调反的是-r。它要的是“连续匹配的 token 数”,不是文件行数,也不是百分比。很多 GUI 封装把它标成 “min run length” 或 “detection size”,填的时候一定要先弄清单位。-t填的是 0 到 100 的百分比,不是 0 到 1 的小数——有人填过 0.3,结果所有相似度大于 0.3% 的对全显示出来,报告上万行。参数填完之后,先拿一个只有两个文件的小目录试跑,用肉眼确认输出和预期一致,再上全量。这一步是验证 GUI 有没有把参数正确传给核心程序的关键,也是发现封装 bug 最快的路径。
如果 GUI 不支持-e排除清单,不必硬解配置文件。更省事的做法是:把公共模板文件从待查目录里移出去,单独放一个_common目录不参与扫描,再建一个只包含学生提交文件的临时目录跑查重。这样既绕开了 GUI 的功能缺口,也让报告里的百分比更有解释力。
4. 报告解读与阈值判定:相似度百分比怎么用才不冤枉人
4.1 读报告的三个关键字段:百分比对、片段坐标、覆盖占比
命令行核心输出的文本报告结构大致如下(字段顺序在不同版本里略有差异,核心信息一致):
sim_c -t 30 -r 5 Comparing: A021.cpp, A033.cpp, B002.cpp A021.cpp and A033.cpp: 48% (A021: 52%, A033: 45%) fragment: 41 tokens, lines 23-64 in A021.cpp, lines 31-72 in A033.cpp A021.cpp and B002.cpp: 12% (A021: 11%, B002: 14%)第一行百分比是这对文件的整体相似度,括号里是两个文件各自的覆盖占比——A021 有 52% 的 token 被匹配,A033 是 45%。不对称是正常的,因为两份文件长度不同,同一次匹配在长文件里占比低、在短文件里占比高。第二段 fragment 是复核凭证:哪两个文件、多少 token、各自在哪几行。我拿到报告时,第一眼永远看 fragment 的位置分布,而不是整体百分比。如果所有匹配都集中在文件尾部的公共工具函数区,说明是共享代码;如果从文件中部到结尾连续三四个 fragment 咬合,那才是真问题。
这里再强调一次星型结构:A 和 B 都抄 C 时,报告会出现 A-C 高、B-C 高、A-B 低。所以不要只盯着百分比最高的那一对文件看,把所有超过阈值的对先列成一张表,按“公共源文件”角度再扫一遍,能发现很多隐藏关系。
4.2 阈值分档:30% 是分水岭,60% 基本坐实
场景 建议阈值 处理动作 课程作业初筛 30% 进入人工复核 课程作业坐实 60% 直接列为重点检查对象 项目交接审查 20% 更低阈值,防止改名后混入 开源合规审查 不用总分 只看匹配片段本身课程作业场景里 30% 是我习惯的初筛分水岭:低于 30% 的文件对,除了极个别的短文件,大部分是公共模板和相似题设造成的自然重合;30% 到 60% 是灰色地带,需要人工打开两个文件并排看;60% 以上通常是“整段复制 + 少量变量改名”,可以直接纳入定性流程。项目交接或外包代码审查我会把阈值降到 20%,因为这种场景的目标不是抓“完全一样的复制”,而是抓“改了名字但结构没动”的继承代码——阈值太高会把这一类漏掉。开源合规审查完全反过来,百分比不重要:哪怕总相似度只有 15%,只要有一个 500 行的函数和某个 GPL 项目的实现逐行匹配,风险就比 60% 的课程作业严重得多。这种场景把 fragment 导航当主角,百分比当配角。
4.3 GUI 与命令行互查:怀疑报告时用核心程序复跑
GUI 和命令行核心是同一个引擎,参数传对时结果必须一致。所以收到一份可疑报告时,我做的第一件事不是改参数,而是用命令行把同一批文件、同一组参数复跑一遍,对比两份报告。数字一致,说明 GUI 的封装没问题,报告可信;数字不一致,优先查两件事:GUI 是否在背后帮你加了排除规则,或者 GUI 是否递归扫描了子目录导致文件集合比命令行多。定位到这个层面,问题基本就清晰了——不是查重算法坏了,是“喂给算法的文件集合”和“你心里想的文件集合”不一致。
还有一个低成本的双引擎交叉验证:当 sim_c 给出 30% 左右的可疑分数,但匹配片段集中在注释和文档字符串里时,用 sim_text.exe 对同一对文件跑一次纯文本查重。sim_text 不做语法归一化,它把文本当普通 token 流比较;如果 sim_text 的匹配片段与 sim_c 的重合度很低,说明刚才的相似来自公共注释和模板框架,属于误报。这个“怀疑报告就先复跑”的习惯,能省掉大量和工具搏斗的时间。
5. 免安装版避坑:运行库、中文路径与误报漏报的五个现场
5.1 启动阶段:缺运行库、被杀毒拦截、核心文件缺失
现场一:双击 GUI 没反应,或者弹窗提示“找不到 VCRUNTIME140.dll”“无法启动此程序,因为计算机中丢失 MSVCP140.dll”。原因基本是核心程序用 MSVC 编译,动态链接了 VC++ 运行库,而免安装版通常不会把这部分运行库打进去。解决方法是先装一遍 Microsoft Visual C++ 2015-2022 Redistributable (x64),装完再双击;如果还崩,就别折腾 GUI 了,直接在 cmd 里跑sim_c.exe -h,核心能跑就说明问题出在前端而不是运行库。
现场二:解压后被杀毒软件直接隔离删除,或者 Windows SmartScreen 弹蓝色警告。原因很好理解:免安装版是无代码签名的自解压程序,行为特征和捆绑包有点像,启发式规则容易误判。解决方法是把整个目录加入信任区,然后用 zip 格式重新分发——zip 比自解压 exe 的误报率低很多。在团队内部,更靠谱的做法是把这种免安装工具放进内部软件源统一管理,免得每次换机器都有人被拦。
现场三:GUI 能正常打开,但点“开始查重”后报告为空或直接闪退。先核对目录里到底有没有 sim_py.exe、sim_java.exe——很多精简版只带 C/C++ 核心。另一个高频原因是免安装包被解压到了含空格的路径,GUI 拼接命令行参数时把路径切断,核心程序找不到文件。把整个目录挪到C:\CheckTool这种无空格路径下再跑,能解决一大半“莫名其妙闪退”的问题。
5.2 结果阶段:中文路径、公共代码、编码导致误报漏报
现场四:命令行传中文目录,提示找不到文件,或者报告里的文件名全是乱码。原因是老版本核心程序按系统 ANSI 编码解析路径和文件名,中文目录在参数传递过程中被截断。别指望它能修,直接批量把待查文件复制到英文路径,文件名统一改成学号或编号,这是最快的解。报告乱码不影响查重数据本身,但要归档留证时很难看,顺手改掉更省心。
现场五:所有文件两两相似度都超过 40%,报告完全失去区分度。原因是每个作业都#include同一套头文件、粘贴了同一份课程模板,公共 token 全被计入匹配。解决方法是把公共模板提取出来,用-e common.txt排除;如果 GUI 不支持排除,就按 3.3 说的把公共文件移出待查目录。这里有个判断技巧:排除公共代码后分数从 50% 掉到 20% 以下,说明原本的相似主要来自框架;如果排除后仍然在 40% 以上,那就是真实复制,不用再怀疑工具误报。
现场六:两份明显复制的 Python 代码分数很低,甚至报告里找不到某个.py文件。原因通常是文件带 UTF-8 BOM,或者缩进混用了 tab 和空格,sim_py 的词法分析器中途解析异常,直接把文件跳过。解决方法是先跑一个预处理脚本,统一转码并清理缩进:
from pathlib import Path for p in Path("D:/submissions").rglob("*.py"): data = p.read_bytes().decode("utf-8-sig", errors="replace") p.write_text(data.replace("\t", " "), encoding="utf-8")utf-8-sig解码会同时吃掉 BOM,replace把坏字符替换成占位符而不是中断处理,tab 统一成四个空格能减少混合缩进导致的解析波动。这个脚本只做文本层面的规整,不改代码语义,跑完再执行 3.2 的 sim_py 命令。如果某个文件仍然解析异常,就用 sim_text.exe 对它单独兜底,别让一个坏文件影响整体结论。
6. SIMGUI 进阶:两轮阈值法与批量查重的校准技巧
6.1 两轮阈值法
直接设一个固定阈值做全量筛查,总会两头受气:阈值太高漏掉灰色地带,阈值太低输出几百页没法看。我现在的做法是两轮:第一轮-t 20全量扫一遍,把结果落成 CSV;第二轮把超过 60% 的对单独复查,20% 到 60% 的区间按 fragment 分布抽样看。第一轮负责“不漏”,第二轮负责“定责”,中间灰色地带用人工判断而不是让工具替你拍板。
6.2 批量比对与校准实验
几十个文件有时需要批量调用核心 exe 并把分数收集成表格,可以用一小段 Python 脚本完成:
import glob, re, subprocess, csv core = r"C:\CheckTool\sim_c.exe" files = glob.glob(r"D:\submissions\*.cpp") rows = [] for i in range(len(files)): for j in range(i + 1, len(files)): out = subprocess.run( [core, "-t", "20", "-r", "5", files[i], files[j]], capture_output=True, text=True, errors="replace" ).stdout m = re.search(r"(\d+)%", out) if m: rows.append([files[i], files[j], int(m.group(1))]) with open("result.csv", "w", newline="") as f: w = csv.writer(f) w.writerow(["a", "b", "similarity"]) w.writerows(rows)这是示意脚本,正则的字段要按你手上版本的输出格式调,但两两比对的思路通用。注意复杂度是 O(n²),几十个文件没问题,几百个就要先按目录或题目分组再算。脚本跑完前,先做一次校准实验:拿一份干净的参考程序,生成三个变体——只删注释重排缩进的、系统改变量名的、改常量并拆函数的,放进一个小目录单独跑。看这三份变体相对原文件的分数梯度是否符合直觉:第一个变体应接近满分,第二个明显下降但能识别,第三个可能跌破阈值。这组实验能告诉你手上这个包的实际灵敏度,也顺便验证-r和-t取值是否适合你的题目场景。我现在每次拿到新版本的免安装 SIMGUI,第一件事就是在 cmd 里跑sim_c.exe -h,再跑一遍校准样本,确认黑匣子的行为和预期一致,才敢让它进正式流程。希望帮到你。
本文还有配套的精品资源,点击获取