首页 / 资讯中心 / 文章详情

SIMGUI代码查重:基于SIM算法的C++/Python双版本免安装工具

SIMGUI代码查重:基于SIM算法的C++/Python双版本免安装工具 ★ FEATURED ARTICLE
简介这款免安装版SIMGUI是面向C与Python开发者的代码查重工具基于Electron与Element UI构建内置SIM相似性检测算法无需安装依赖即可运行。压缩包共87个文件约70MB主要包含exe主程序、dll与pak运行组件、bin数据文件以及png操作截图其中pak多用于界面多语言支持exe为程序入口dll负责运行时依赖png为操作指引截图结构清晰解压后直接使用。已有529人学习下载适合教学作业查重、学术论文代码比对及团队代码质量审查等场景。使用时通过文件选择器导入代码可调整查重灵敏度、忽略大小写/空格/注释等参数点击检测即可定位重复代码段并以可视化列表展示比对结果支持导出完整报告。免安装特性省去环境配置环节尤其适合需要批量检测Python/C作业相似度的师生或希望快速梳理项目冗余代码的开发者。1. 收代码收到手软时SIMGUI 是你最该先装的那个工具期末或者季末收代码的日子我见过太多人抱着几十个压缩包一个个解压再用编辑器的”在文件中查找”肉眼比对两个文件的重复片段。一个人改个变量名、把函数顺序调一调肉眼根本看不出来等提交到查重系统里被打回师生两边都难受。这个标题里的 SIMGUI说的是把 SIM 相似度检测算法包装成图形界面同时提供 C 编译好的核心程序和 Python 调用接口做成免安装绿色版解压就能用。它解决的就是代码初筛问题把”哪些提交大概率互相抄了”先筛出来让人把精力放在复核而不是大海捞针上。适合三类人收大作业和毕设的教师助教、做代码审计的技术负责人、以及想在上交前自己预检一遍的学生。2. 为什么偏偏是 SIM查重算法选型与 C / Python 双版本分工2.1 先分清 MD5、diff 和 SIM 各自的适用边界很多人一提到查重第一反应是比对 MD5或者用 diff 看两文件差异。MD5 只能告诉你两个文件是否完全相同只要少一个空格、换一个变量名哈希值就完全变了对”改改就交”的场景毫无威慑力。diff 是逐行比较它能列出差异行但它天生是给版本控制设计的目标是展示”改了哪几行”而不是量化”整体有多像”。两份代码如果函数顺序被整体打乱diff 会输出一大堆”删除新增”看起来像完全重写实际上只是挪了个位置。SIM 算法不一样。它不关心顺序是否完全一致而是去找两个文本之间所有的公共连续片段也就是”运行run”再用公共片段的总长度除以参考文本长度算出相似度百分比。打个粗糙的比方它把一段代码拆成连续的小块凡是两边都对得上的块都记下来最后加总。所以哪怕你给人家的代码换了变量名、改了函数名、调整了声明位置只要句子和结构还是照抄的SIM 给出的相似度依然会很高。这正是代码查重需要的”宽松匹配”。表格对比一下工具能查什么查不出什么适合场景MD5完全相同的文件任何微小改动文件完整性校验diff逐行差异结构搬移、改名换序代码审阅、版本对比SIM公共连续片段量化相似度语义重写的两套代码抄袭初筛、重复度检测2.2 SIM 的相似度是怎么算出来的公共片段与归一化SIM 的核心计算过程可以简化成三步。第一步把输入文本按行做切分每行再切成更细的单元这个单元可以是词、是符号、也可以直接是字符取决于你用的是哪个变体和什么参数。第二步在两个文本之间寻找公共连续的单元序列凡是能连起来匹配的都算一个 run每个 run 的长度被记录下来。第三步把所有 run 的长度加总除以参考文本的总长度得到相似度百分比。这个”除以参考文本”的细节很关键。同样一对文件用 A 做参考还是用 B 做参考得到的百分比可能不一样因为如果 A 是 1000 行、B 是 2000 行公共部分占 A 的比例和占 B 的比例自然不同。所以命令行版的 sim_c 才需要显式指定哪个是参考文件、哪个是测试文件。做批量初筛时习惯上是把”疑似被抄的原始版本”当作参考把”学生交的版本”当作测试。SIM 对空白和换行的容忍度也比较高因为它匹配的是单元序列而不是原始字符串。这个特性是查重需要的但也带来一个副作用代码格式化工具跑一遍之后的”美化版”照样是高相似度反过来”故意打乱空行、拆行长表达式”并没有太多作用。理解这一点后面调试结果时就不会误判。2.3 C 和 Python 两个版本怎么分工核心与外壳标题里同时出现 C 和 Python不是冗余而是两个版本各自的位置不同。C 的 sim_c 是算法核心负责跑匹配速度很快几十 KB 到几 MB 的源码文件都是毫秒级完成。但 C 版本不好改要在 Windows 上编译需要装 MinGW 或 Visual Studio 工具链改一行算法逻辑就得重新编译一次它对大多数老师来说门槛太高。Python 版本的价值在于”能嵌进流程”。你可以写一个几十行的脚本遍历一个目录里的所有 .cpp/.py 文件两两调用核心计算生成一个 CSV 报告把所有相似度超过阈值的配对列出来。这个批量能力是 GUI 版没有的。常见的做法是Python 脚本用 subprocess 调用 C 编译好的 sim_c.exe自己只负责文件枚举、结果解析和报告输出。这样性能由 C 保证灵活度由 Python 提供两边各干各擅长的事。至于 SIMGUI本质上就是给这个流程包了一层界面左边选参考文件、右边选待查文件、中间放一个阈值输入框点一下按钮调用核心最后把相似度百分比显示出来。免安装版则是把编译好的程序、依赖的动态库、可选的 Python 脚本和说明文件打成一个压缩包不写注册表、不装服务解压后直接运行。对机房电脑和没有管理员权限的办公电脑来说这是最稳妥的部署方式。3. 把免安装版跑起来从解压到批处理的最小操作路径3.1 免安装版的目录结构长什么样常见的免安装版压缩包解开后通常是这样一个结构一个可执行文件比如 sim_c.exe、一个带界面的程序比如 simgui.exe 或者 simgui.pyw、一个说明文档外加一个 data 目录放临时文件。C 版可能还会带一两个运行库 DLL比如 vcruntime140.dll。如果缺这个文件双击会直接报错后面第 5 章会专门讲。我一般建议先把包解压到一个纯英文路径下避免之后因为中文路径或空格出问题路径直接写成 D:\sim 或者 C:\checker 都是好习惯D:\sim\ ├─ sim_c.exe # 命令行核心程序C 编译 ├─ simgui.exe # 简单图形界面封装 ├─ run.bat # 免安装启动器 ├─ batch_check.py # Python 批量调用脚本 └─ README.txt不要小看这个结构。命令行程序在服务器和批处理里用GUI 程序给不熟悉命令行的同事用Python 脚本给需要二次开发的人用三者各司其职。如果你拿到的压缩包里没有 batch_check.py 这种脚本完全可以自己写后面 3.4 节会给一个可以直接抄的版本。3.2 命令行方式最可靠的第一个动作不管是哪一家的免安装版第一个最小验证动作永远是跑命令行而不是双击 GUI。打开 cmd进入解压目录执行下面这行sim_c.exe -p 30 -s reference.cpp submitted.cpp这里 -p 30 表示相似度超过 30% 才输出结果-s 表示只输出相似度数值摘要不把每一处公共片段都打印出来。跑完你会看到类似32%这样的输出这个数字就是提交文件相对参考文件的相似度。如果结果超过你的预期再不加 -s 重新跑它能打印出具体哪些行是公共的方便肉眼复核。命令行还有一个隐藏好处它不吃 GUI 那套控件状态路径带空格、文件编码奇怪、文件特别大这些在命令行下都很容易暴露问题。我第一次用某个绿色版时GUI 点按钮没反应反而是命令行报了一个明确的文件打不开错误一下子定位到是路径中有中文。从那以后我的排查顺序永远是命令行优先。3.3 Python 调用核心程序把结果变成结构化数据命令行适合单次比对但收一百份作业时不能一个个手动跑。写一个 Python 脚本用 subprocess 反复调用 sim_c.exe把输出解析成结构化结果import subprocess import os def sim_score(reference, target, threshold30): 调用 sim_c 计算相似度返回百分比数值 cmd [sim_c.exe, -p, str(threshold), -s, reference, target] result subprocess.run(cmd, capture_outputTrue, textTrue) # 典型的输出形如 45%也可能带路径前缀 for token in result.stdout.strip().split(): if token.endswith(%): return int(token.strip(%)) return 0 if __name__ __main__: print(sim_score(ref.cpp, stu_01.cpp))这段代码有两处值得说明。第一subprocess.run 用 capture_output 接住程序输出避免命令行结果直接打到屏幕上这样脚本才能拿到返回值做后续判断。第二解析百分比时用了”找到以 % 结尾的 token”这种宽容策略而不是死板地按第 0 个元素解析因为不同编译版本的输出格式可能略有差异。如果你的 sim_c 输出里百分比带小数把 int() 改成 float() 就行。这种调用方式还有一个好处C 核心进程跑崩溃了Python 不会跟着崩。subprocess 每个进程是隔离的某个文件编码异常导致核心读不了最多返回异常退出码Python 捕获到之后可以把这个文件丢进错误清单继续处理剩下的文件。3.4 给 GUI 版包一个可靠的启动器免安装版最容易翻车的环节是用户不知道双击哪个文件。写一个 run.bat 放在根目录让使用者只认这一个入口echo off chcp 65001 nul cd /d %~dp0 start simgui.exe %*三行各自有作用。chcp 65001 把控制台代码页切到 UTF-8避免 README 里中文说明乱码cd /d %~dp0 把工作目录切到 bat 所在的目录这样不管用户从哪个路径双击程序都能找到同目录的依赖文件最后 start 开 GUI。这个启动器的价值在配好环境后几乎察觉不到但在没配好的电脑上它能把”为什么双击没反应”这类问题直接消灭掉。4. 参数与预处理让查重结果从”能跑”变成”可信”4.1 三个必调参数阈值、输出模式、参考方向用 SIM 做初筛参数不用多但每个都要弄清楚。最重要的就是相似度阈值 -p默认值一般在 30 左右意思是不足 30% 的配对直接不显示。收作业时我习惯把阈值设在 50 以上因为低于 50 的配对大多是模板代码和公共头文件造成的虚高逐个看过去浪费时间。反过来如果是在做论文级代码核查想宽进严出可以把阈值降到 20宁肯多筛一些出来给人眼判断。第二个要理解的是输出模式 -s。加了它程序只给一个数字不加会把公共片段和行号全打出来。第一次用的人往往看着满屏公共片段不知所措实际上你应该始终配合 -s 用先用数字排序锁定嫌疑对再去掉 -s 单对复核。这才是正确的工作流。第三个经常被忽略的是参考文件与测试文件的顺序。前面说过SIM 是用公共部分除以参考文件长度那么谁做参考结果就偏向谁。做批量检测时如果你拿学生的 A 作业做参考去比 B 作业又拿 B 做参考去比 A两次出来的百分比不会一样。批量脚本里必须固定规则比如始终用先枚举到的文件做参考并在报告里同时列出双向的两个数值。4.2 查代码之前先做一次文本预处理直接拿原始代码喂给 SIM结果会虚高。原因是每个项目的模板代码、公共头文件、自动生成的注释都算进了公共长度里。我一般会先对代码做一步轻量预处理去掉空行、去掉注释、把制表符换成空格。这一步不追求把代码”改写”只是为了减少公共噪声。用 Python 做预处理很简单下面是去掉注释和空行的最小版本针对 C/C 和 Python 都做了处理import re def clean_source(code: str) - str: # 去掉 // 行注释和 /* 块注释粗略版够用 code re.sub(r//[^\n]*, , code) code re.sub(r/\*.*?\*/, , code, flagsre.S) # 去掉 Python 的 # 注释注意保留字符串里的 # lines [] for line in code.splitlines(): stripped line.strip() if stripped.startswith(#): continue lines.append(line) return \n.join(lines)注意这段代码注释里写着”粗略版”。为什么说够用因为它不处理字符串里的 // 和 #比如一个 URL 写在字符串里会被误删但对查重来说误删字符串内容对最终相似度影响极小可以接受。预处理的目标不是让代码变得”干净”而是让查重结果更聚焦在实质性代码上。如果你要处理的是 Python 代码字符串里的 # 还挺常见可以把正则改成只删除行首空白后的 # 注释行而不是删除所有 # 开头的行。做完预处理再喂给 sim_c你会观察到相似度明显下降那个下降量基本就是模板代码贡献的虚高部分。如果你发现下降幅度特别大先别急着高兴去看看到底是模板多还是两个人真的写得像。4.3 批量两两对比并生成 CSV 报告批量查重的最终产物是一张表。下面这段脚本遍历一个目录下所有 .cpp 和 .c 文件两两组合调用 sim_c把结果存进 CSVimport subprocess, os, glob, csv, itertools def score(reference, target): p subprocess.run([sim_c.exe, -p, 0, -s, reference, target], capture_outputTrue, textTrue) for tok in p.stdout.split(): if tok.endswith(%): return float(tok.strip(%)) return 0.0 files sorted(glob.glob(submissions/*.cpp)) with open(report.csv, w, newline) as f: w csv.writer(f) w.writerow([ref, target, score]) for ref, tgt in itertools.combinations(files, 2): s score(ref, tgt) if s 40: # 只记录超过 40% 的嫌疑对 w.writerow([os.path.basename(ref), os.path.basename(tgt), s])这里把 -p 设成 0是因为筛选用途下你想要拿到所有配对的实际分数再由 Python 端做过滤。阈值写在 Python 里比写在核心程序里更灵活你可以在跑完一遍之后不重新比对只改过滤条件再生成一份新报告。itertools.combinations 保证每对文件只比一次不会出现 A 对 B 和 B 对 A 都跑一遍的浪费。跑完打开 CSV按 score 列降序排序嫌疑对一目了然。这个报告可以直接发给复查的人把可疑文件对挑出来逐个看 diff。到这一步查重初筛的基本盘就立住了C 核心负责算Python 负责编排CSV 承载结果整个过程不依赖任何在线服务全部本地完成。5. 避坑与排查5 个最常见的翻车现场5.1 免安装版双击没反应运行库缺失现象双击 simgui.exe鼠标转两圈就没动静或者直接弹窗提示”找不到 VCRUNTIME140.dll”。原因免安装版为了减小体积通常会动态链接 Visual C 运行库而目标机器没有安装 Microsoft Visual C Redistributable。这不是工具坏了是系统的公共依赖缺失。解决优先装运行库安装包只有几十 MB装完重启程序即可。如果使用场景是机房或者不允许装软件的机器那就在解压包里补上缺失的 DLL放在 exe 同目录下这样能绕开系统目录的权限问题。这个问题的排查方法也简单从别的机器拷贝一个能正常运行的完整目录过来如果拷贝过来还是报同样的错那就是缺系统级运行库如果拷贝过来能跑说明是原压缩包的 DLL 不完整。5.2 相似度虚高两个不相干的文件也有 40%现象拿两个明显不同的课程设计项目去比相似度居然有 40% 多吓人一跳。原因多数是公共基础文本造成的。同一个老师的作业要求里通常会给出统一的头文件、结构体定义、注释模板再加上代码风格都按同一种缩进习惯写public/private 这些关键字在每个类里必然重复出现。SIM 不区分”公共模板”和”实质性抄袭”它只统计公共片段长度。解决先做 4.2 的预处理再去掉题目自带的模板声明最后把阈值调高。如果预处理之后虚高还是存在就需要怀疑是不是两个文件的公共片段集中在几行特别长的相似函数上这时去掉 -s 跑一遍看公共片段具体落在哪几行是模板还是实质逻辑一眼就知道。5.3 Python 版本跑大文件内存泄漏式增长现象用 Python 重写或调用的查重脚本处理几百个文件时越来越慢最后内存占用冲到几个 GB。原因常见的是在读文件时用了 readlines() 把整个文件变成行列表再反复切片拼接SIM 算法本身在 Python 里对长文本做匹配复杂度会随文本长度上升字符串拼接又会产生大量临时对象。解决优先用 C 编译的核心程序做计算Python 只做外围调度这是最省心的方案。如果一定要纯 Python 实现就避免一次性读入大文件改成按块读取并且只用字节流做匹配不要反复 decode 成字符串。实时监控内存可以用 Python 的 tracemalloc 模块定位到具体哪一步在涨一般都能查到是某个正则对超长行做了灾难性回溯。5.4 GUI 版选了文件但结果一直是 0现象文件都选上了点开始结果永远是 0%命令行跑同一对文件却很正常。原因十有八九是 GUI 在拼命令时把一个文件路径里包含的空格断开了。比如文件放在 C:\Users\My Documents\code\a.cpp 这种路径GUI 直接把它拼进命令行没有加引号核心程序只收到了前半段。解决把文件放到纯英文无空格的路径下再试一次。如果必须支持中文和空格路径就要在 GUI 或脚本里给路径加引号即 subprocess 调用时不要用字符串拼接而是用列表参数让系统自己处理引号问题。这是一个很典型的 GUI 封装翻车点本质上是界面层拼命令不规范跟算法无关。5.5 大改之后相似度还是 80%现象对方明显改了变量名、删了注释、把函数顺序换了个遍结果查重还是 80% 出头。原因SIM 本来就这样。它抓的是连续公共片段只要函数体的逻辑结构和语句顺序没变改名删注释都不会显著降低相似度。这既是它的优势也是它的局限它能抓住”换皮不换里”但对”换了种写法实现同样功能”毫无办法。解决不要指望 SIM 能识别语义重写。遇到相似度高但”看起来改了很多”的正确姿势是把两个文件并排打开对照公共片段逐段看。SIM 的价值是把 100 份里最可疑的 8 份筛出来不是替你下最终结论。查重工具做的是初筛人做的才是判断。6. 进阶把查重工具变成你的初筛流水线6.1 用”自比”校准阈值很多人拿到工具后第一件事是拿真实作业开跑其实在此之前应该先做一步校准拿同一个文件跟它自己比理论上相似度是 100%。然后做两个实验——把一份代码里的变量名全部批量替换看看分数掉到多少再删掉一半函数看看分数掉到多少。这样你才能知道你设的阈值 50 到底代表”变量名级别的修改”还是”删了一半还能查出来”。我习惯在一个小目录里放几组自己造的样本跑一遍记录分数以后换了机器、换了编译版本都拿这个目录回归测试一次。6.2 用伪造样本来验证你的批处理脚本批量脚本最怕的不是逻辑错而是看着对但实际输出不完整。写一个假样本集验证是最省事的办法在 submissions 目录里放 4 个文件其中两个完全一样一个改了三分之一另一个完全不同跑完看 CSV 里能不能精准列出前两组、过滤掉最后一组。验证通过后再上真实数据。这个习惯帮我抓出过两次脚本 bug一次是 glob 通配符没匹配到子目录一次是 CSV 写入时把高分项漏掉了都是假样本暴露的。6.3 把初筛结果交给人工复核最后要交代的是工作流的位置。SIMGUI 这类工具适合放在流程的最前面用最低成本把嫌疑面从一百份缩到十份但它的输出不能直接当证据用。我现在的做法是自动初筛 → 人工复核可疑对 → 需要时再用差分对比工具确认具体复制范围。整个链路里SIM 只负责第一道线。你别指望靠一个百分比就向其他人交代能把公共片段具体到函数级别才算是真正查清楚了。用这套流程处理过几次上百人的课程后我的感受是工具本身不是重点怎么把你的阈值、你的预处理规则、你的复核标准固定成一套可重复的流程才是省时间的核心。每学期开课之前花半天把脚本调好后面几周收作业时就再也不用半夜对着两个文件揉眼睛了。希望这些路径和坑能帮你少走一段弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站