首页 > 其他分享 >关于 Special Judge 的编写和本地测试

关于 Special Judge 的编写和本地测试

时间:2024-01-20 11:47:21浏览次数:25  
标签:std const int system return Judge 编写 include Special

最近有几位同学来问我 Special Judge 怎么写?为了让大家可以写出 Special Judge 方便在本地调试和对拍,我就想写一篇文章来介绍 Special Judge。

Special Judge 是什么?有什么用?

大家可以先看这样一篇文章:Special Judge - OI Wiki (oi-wiki.org)

Special Judge(简称:SPJ,别名:checker)是当一道题有多组解时,用来判断答案合法性的程序。

—— OI - Wiki

Special Judge 的写法

下面这两篇文章是关于 Special Judge 的写法:

因为洛谷使用的是 CodeforcesTestlib,所以就按照 Testlib 的 SPJ 写法即可。

但是本篇文章的重点不在于怎么编写 Special Judge,因为上面的文章都给出了很详细的写法,而是如何利用它来进行本地调试和对拍

怎么利用 Special Judge 进行对拍

鉴于 CodeForces 自带的 Testlib 不好用,也有可能是因为我不会用,所以我手写了一个 Testlib 来方便调试。

只需要把下面的代码保存为 testlib.h 和 checker 在同一文件夹下即可。

//the code is from chenjh
#include<cstdio>
#include<string>
#include<cassert>
#include<regex>
#include<windows.h>
struct TestLib{
    FILE *f;
    char readChar(){char c;std::fscanf(f,"%c",&c);return c;}
    char readChar(char ch){char c=readChar();assert(c==ch);return c;}
    char readSpace(){char c=readChar();assert(c==' ');return c;}
    void unreadChar(char ch){/*暂且不会实现。*/}
    std::string readToken(){char s[1<<20];std::fscanf(f,"%s",s);std::string str=s;return str;}
    std::string readToken(std::string regex){std::string s=readToken();assert(regex_match(s,std::regex(regex)));return s;}
    std::string readWord(){return readToken();}
    std::string readWord(std::string regex){return readToken(regex);}
    long long readLong(){long long x;std::fscanf(f,"%lld",&x);return x;}
    long long readLong(const long long&L,const long long&R){long long x=readLong();
    assert(L<=x && x<=R);return x;}
    int readInt(){int x;std::fscanf(f,"%d",&x);return x;}
    int readInteger(){return readInt();}
    int readInt(int L,int R){int x=readInt();assert(L<=x && x<=R);return x;}
    double readDouble(){double x;std::fscanf(f,"%lf",&x);return x;}
    double readReal(){return readDouble();}
    double readDouble(const double&L,const double&R){double x=readDouble();
    assert(L<=x && x<=R);return x;}
    std::string readLine(){char s[1<<20];std::fscanf(f,"%[^\n]",s);std::string str=s;return str;}
    std::string readString(){return readLine();}
    void readEoln(){char c;std::fscanf(f,"%c",&c);assert(c=='\r'||c=='\n');if(c=='\r')std::fscanf(f,"%c",&c);assert(c=='\n');}
    void readEof(){char c;std::fscanf(f,"%c",&c);assert(~c);}
}inf,ouf,ans;
void registerTestlibCmd(int argc,char* argv[]){
    inf.f=std::fopen("data.in","r");
    ouf.f=std::fopen("data.out","r");
    ans.f=std::fopen("data.ans","r");
}
/*
Color:
    需要 16 进制 0x 前缀。 
    0 = 黑色       8 = 灰色
    1 = 蓝色       9 = 淡蓝色
    2 = 绿色       A = 淡绿色
    3 = 浅绿色       B = 淡浅绿色
    4 = 红色       C = 淡红色
    5 = 紫色       D = 淡紫色
    6 = 黄色       E = 淡黄色
    7 = 白色       F = 亮白色
*/
#ifndef CHECK
    template<typename... Args>void COLOR_PRINT(const int color,const char*s,Args...x){
        HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleTextAttribute(handle,FOREGROUND_INTENSITY|color);
        std::printf(s,x...);
        SetConsoleTextAttribute(handle,FOREGROUND_INTENSITY|7);
    }
#endif
const int _ok=0,_wa=1;
template<typename... Args>void quitf(const int ret,const char*s,Args...x){
    #ifndef CHECK
        std::printf("Score: ");
        if(ret) COLOR_PRINT(0xa,"100\n");
        else COLOR_PRINT(0xa,"0\n");
        std::puts("Checker comment");
        if(ret) COLOR_PRINT(0x4,"wa ");
        else COLOR_PRINT(0xa,"ok ");
        COLOR_PRINT(0x8,s,x...);
        std::putchar('\n');
    #endif
    std::exit(ret);
}
template<typename... Args>void quitp(const double&ret,const char*s,Args...x){
    #ifndef CHECK
        std::printf("Score: ");
        COLOR_PRINT(0xa,"%.1lf\n",ret);
        std::puts("Checker comment");
        COLOR_PRINT(0x4,"wa ");
        COLOR_PRINT(0x8,s,x...);
    #endif
    std::putchar('\n'),std::exit(_wa);
}

因为 void unreadChar(char ch) 暂且还不会实现,我猜你们也不会用到这些函数,如果有大佬会实现,欢迎私信给我提出指导意见以及建议。

下面我解释一下我手写的 Testlib 各部分的作用和一些函数的意思:

函数 void registerTestlibCmd(int argc,char* argv[])

这个函数里初始化了文件的读入流。

因为要同时读入多个文件,所以我使用了 cstdio 库中的 fopen 函数。

函数的原型是 std::FILE* fopen( const char* filename, const char* mode );

具体使用方法可以参考 std::fopen - cppreference.com

cassert 库中的 assert() 函数

在标头 <cassert> 定义:

#ifdef NDEBUG
#define assert(condition) ((void)0)
#else
#define assert(condition) /*implementation defined*/
#endif

如果传进该函数的参数为假,则会返回一个非 \(0\) 值(即 RE 段错误)。

cstdio 库中的 fscanf() 函数

在标头 <cstdio> 定义:

int fscanf( std::FILE* stream, const char* format, ... );//从文件流 stream 读数据,按照 format 转译并存储结果于给定位置。

上面这个函数的使用方法其实和 scanf() 差不多,只需要多加一个参数(即文件流)即可。

Special Judge 的对拍程序

Special Judge 的对拍程序

首先我们以 AT_dp_f LCS 这一道题为例。

首先将上面我手写的 testlib.h 保存(注:头文件不需要编译!)。

根据上面 SPJ 的编写方法,我们可以写出这样一个 checker.cpp(请注意请和 testlib.h 放在同一个文件夹下方)并用 C++14 标准进行编译(最好开启无限栈):

//the code is from chenjh
#include "testlib.h"
#include<string>
#define WA quitf(_wa,"WA!")
#define AC quitf(_ok, "Correct.")
using namespace std;
string s,t,ou,jans;
int main(int argc,char* argv[]){
	registerTestlibCmd(argc,argv);
	s=inf.readToken();t=inf.readToken();
	ou=ouf.readToken();jans=ans.readToken();
	if(ou==jans){AC;return 0;}
	if((int)ou.length()!=jans.length()){WA;return 0;}
	int l=ou.length(),l1=s.length(),l2=t.length(),j=0;
	for(int i=0;i<l;i++){
		for(;ou[i]!=s[j] && j<l1;j++);
		if(j>=l1 || ou[i]!=s[j]){WA;return 0;}
	}
	j=0;
	for(int i=0;i<l;i++){
		for(;ou[i]!=t[j] && j<l2;j++);
		if(j>=l2 || ou[i]!=t[j]){WA;return 0;}
	}
	AC;
	return 0;
}

接着把出现错误需要对拍的代码命名为 code.cpp,正确的代码命名为 std.cpp,并将它们都进行编译(不需要在代码中提前加入文件重定向)。

code.cpp(这里选用了来自 @hky03118002 的 \(\color{#B3E600}{72}\) 分代码):

//the code is from hky(st20242008).
//Submission number is #502175.
#include<bits/stdc++.h>
using namespace std;
string ans[2][3010];
int f[2][3010],i,j,lena,lenb;
char a[3010],b[3010];
struct B{
	int i,j,f;
}pre[3010][3010];
void print(int i,int j){
	if(i<0||j<0||i==0&&j==0)return;
//	printf("%d %d\n",i,j);
	if(pre[i][j].i>0||pre[i][j].j>0)print(pre[i][j].i,pre[i][j].j);
	if(pre[i][j].f)printf("%c",a[i]);
}
main(){
	scanf("%s",a);
	scanf("%s",b);
	lena=strlen(a);
	lenb=strlen(b);
	for(i=0;i<lena;++i){
		for(j=0;j<lenb;++j){
			if(j>0&&f[(i&1)^1][j]<f[i&1][j-1]){
				f[i&1][j]=f[i&1][j-1];
				pre[i][j]={i,j-1};
			}
			else{
				f[i&1][j]=f[(i&1)^1][j];
				pre[i][j]={i-1,j};
			}
			if(a[i]==b[j]){
				if(j>0&&f[i&1][j]<=f[(i&1)^1][j-1]+1){
					f[i&1][j]=f[(i&1)^1][j-1]+1;
					pre[i][j]={i-1,j-1,1};
				}
				else{
					pre[i][j].f=1;
				}
			}
		}
	}
	if(a[0]==b[0])printf("%c",a[0]);
	print(i-1,j-1);
}

std.cpp(选用了我的 \(\color{#00CC00}{100}\) 分代码):

//the code is from chenjh(c1120241702)
#include<bits/stdc++.h>
using namespace std;
char s1[3005],s2[3005];
int dp[3005][3005],p[3005][3005];
void print(int x,int y) {
	if(x==0||y==0)return;
	if(p[x][y]==1)
		print(x-1,y-1),putchar(s1[x]);
	else if(p[x][y]==-1)print(x-1,y);
	else print(x,y-1);
}
int main(){
	cin>>(s1+1)>>(s2+1);
	int l1=strlen(s1+1),l2=strlen(s2+1);
	for(int i=0;i<=l1;i++) dp[i][0]=0;
	for(int i=0;i<=l2;i++) dp[0][i]=0;
	for(int i=1;i<=l1;i++){
		for(int j=1;j<=l2;j++){
			if(dp[i-1][j]>=dp[i][j-1])p[i][j]=-1;
			dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			if(s1[i]==s2[j])
				dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1),p[i][j]=1;
		}
	}
	print(l1,l2);
//	printf("%d\n",dp[l1][l2]);
	return 0;
}

将制造数据的代码命名为 maker.cpp 然后进行编译(同样也不需要在代码中提前加入文件重定向)。

//the code is from chenjh
#include<cstdio>
#include<string>
#include<ctime>
#include<cstdlib>
using namespace std;
int rand(int l,int r){return 1ll*rand()*rand()%(r-l+1)+l;}
string lcs="";
int main(){
	unsigned int *seed=new unsigned int;
	srand(time(0)*(*seed+1));
	delete seed;
	int llen=rand(1,500);
	for(int i=0;i<llen;i++) lcs+=(char)rand('a','z');
	int nowlen=3000;
	for(int i=0;i<llen;i++){
		int mlen=rand(1,(3000-llen)/llen);
		putchar(lcs[i]);
		for(int j=0;j<mlen;j++) putchar(rand('a','z'));
		nowlen-=mlen+1;
	}
	while(nowlen--) putchar(rand('a','z'));
	putchar('\n');
	nowlen=3000;
	for(int i=0;i<llen;i++){
		int mlen=rand(1,(3000-llen)/llen);
		putchar(lcs[i]);
		for(int j=0;j<mlen;j++) putchar(rand('a','z'));
		nowlen-=mlen+1;
	}
	while(nowlen--) putchar(rand('a','z'));
	return 0;
}

如果其中运用了随机数,并且种子和时间(例如 time(0) 相关)建议使用一个为初始化过的 int 类型的变量对时间进行相乘再设定为种子。

个人版对拍

接下来就是对拍程序(命名为 check.cpp 并进行编译,根据题目要求修改程序第 \(7\) 排的时间限制)了:

//the code is from chenjh

//用户自行配置: 
//时间限制(单位为毫秒):
const int TimeLimit=2000;

#include<fstream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<string>
#include<windows.h>
using namespace std;
/*
Color:
	需要 16 进制 0x 前缀。 
	0 = 黑色	   8 = 灰色
	1 = 蓝色	   9 = 淡蓝色
	2 = 绿色	   A = 淡绿色
	3 = 浅绿色	   B = 淡浅绿色
	4 = 红色	   C = 淡红色
	5 = 紫色	   D = 淡紫色
	6 = 黄色	   E = 淡黄色
	7 = 白色	   F = 亮白色
*/
template<typename... Args>void COLOR_PRINT(const int front_color,const int back_color,const char*s,Args...x){
	HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(handle, BACKGROUND_INTENSITY | back_color*16 | FOREGROUND_INTENSITY | front_color);
	std::printf(s,x...);
	SetConsoleTextAttribute(handle,FOREGROUND_INTENSITY|7);
}
bool fileExists(const std::string&filename){std::ifstream infile(filename);return infile.good();}//文件是否存在。
void SaveData(int &c){
	string s="data\\data"+to_string(c++);
	string str="copy data.in "+s+".in";
	system(str.c_str());
	str="copy data.ans "+s+".out";
	system(str.c_str());
}
int main(){
	system("md data");system("cls");
	if(!fileExists("maker.exe")) return puts("Can\' t find file maker.exe!"),0;
	if(!fileExists("checker.exe")) return puts("Can\' t find file checker.exe!"),0;
	if(!fileExists("std.exe")) return puts("Can\' t find file std.exe!"),0;
	if(!fileExists("code.exe")) return puts("Can\' t find file code.exe!"),0;
	ofstream outvbs("KillCode.vbs");
	outvbs<<"WSCript.Sleep "<<TimeLimit+200<<'\n';
	outvbs<<"Set WshShell = WScript.CreateObject(\"WScript.Shell\")\nWshShell.Run \"taskkill /im code.exe /f\", 0, True\n";
	outvbs.close();//用来关闭超时的进程。
	int maxc;
	printf("Enter the total number of pairs of shots you wish to obtain:");
	scanf("%d",&maxc);//输入你想要的数据组数。
	for(int t=1,c=1;c<=maxc;t++){
		system("maker.exe > data.in");
		system("std.exe < data.in > data.ans");
		bool sd=0;
		system("start /B KillCode.vbs");
		double tm=clock();
		unsigned int ls=system("code.exe < data.in > data.out");
		tm=clock()-tm;
		system("taskkill /f /im wscript.exe 1>nul 2>nul"),system("taskkill /f /im cscript.exe 1>nul 2>nul");
		printf("\n\nTest#%d: ",t);
		if(tm>TimeLimit) sd=1,COLOR_PRINT(0x7,0x1,"Time Limit Exceeded"),printf("! Time used %.0lf ms.\n",tm);
		else if(ls>0xc0000000u) sd=1,COLOR_PRINT(0x7,0x5,"Runtime Error"),printf("! Time was unavailable!\n");//RE:3221225725
		else if(ls) COLOR_PRINT(0x7,0x0,"Unknown Error!\n"),system("pause");
		else{
			unsigned int rs=system("checker.exe");
			if(rs>0xc0000000u) COLOR_PRINT(0x7,0x5,"Checker Runtime Error"),printf("! Time was unavailable!\n"),system("pause");
			else if(!rs) COLOR_PRINT(0x7,0xA,"Accepted"),printf("! Time used %.0lf ms.\n",tm);
			else if(rs==1)sd=1,COLOR_PRINT(0x7,0x4,"Wrong Answer"),printf("! Time used %.0lf ms.\n",tm);
			else COLOR_PRINT(0x7,0x0,"Unknown Error!\n"),system("pause");
		}
		if(sd) SaveData(c);
		putchar('\n');
	}
	return 0;
}

运行效果图:

效果1

效果2

开始运行程序时输入一个整数(int 类型范围内)表示你需要多少组数据来 Hack 你的代码(提示:请勿作死输入一些很奇怪的数)。

上面的对拍程序会自动将你的代码 WA/TLE/RE 的测试数据(暂不支持判断内存是否超过内存限制)存至同文件夹下的 data 文件夹中。

团队版对拍

需要写一个 src.txt,每一行为对拍程序的名称(不需要后缀名!),然后将所有代码保存到同文件夹下的 src 文件夹中。

不建议输入中文,因为会出现一些奇奇怪怪的编码错误。

//the code is from chenjh
#if __cplusplus < 201103L
#error This code must be enabled with the -std=c++11 or -std=gnu++11 compiler options.
#endif
#include<fstream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<string>
#include<vector>
#include<windows.h>
using std::string;

//用户自行配置: 
//时间限制(单位为毫秒):
const int TimeLimit=2000;
//g++ 编译器路径:
const std::string g__="C:\\Program Files (x86)\\Dev-Cpp\\MinGW64\\bin\\g++.exe";
//编译选项:
const string cp=" -O2 -std=c++14 -Wl,--stack=2147483647" ;
//造数据器名称:
const string maker="maker";
//std 名称:
const string mystd="std";
//SPJ 名称 :
const string checker="checker"; 

/*
Color:
    需要 16 进制 0x 前缀。 
    0 = 黑色       8 = 灰色
    1 = 蓝色       9 = 淡蓝色
    2 = 绿色       A = 淡绿色
    3 = 浅绿色       B = 淡浅绿色
    4 = 红色       C = 淡红色
    5 = 紫色       D = 淡紫色
    6 = 黄色       E = 淡黄色
    7 = 白色       F = 亮白色
*/
template<typename... Args>void COLOR_PRINT(const int front_color,const int back_color,const char*s,Args...x){
    HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(handle, BACKGROUND_INTENSITY | back_color*16 | FOREGROUND_INTENSITY | front_color);
    std::printf(s,x...);
    SetConsoleTextAttribute(handle,FOREGROUND_INTENSITY|7);
}
bool fileExists(const string&filename){std::ifstream infile(filename);return infile.good();}//文件是否存在。
void SaveData(int &c){
    string s="data\\data"+std::to_string(c++);
    system(("copy data.in "+s+".in").c_str());
    system(("copy data.ans "+s+".out").c_str());
}
bool runcode(const string&username){
	std::ofstream outvbs("KillCode.vbs");
	outvbs<<"WSCript.Sleep "<<TimeLimit+200<<"\nSet WshShell = WScript.CreateObject(\"WScript.Shell\")\nWshShell.Run \"taskkill /im "<<username<<".exe /f\", 0, True\n";
	outvbs.close();//用来关闭超时的进程。
    system("start /B KillCode.vbs");
    double tm=clock();
    unsigned int ls=system(("src\\"+username+".exe < data.in > data.out").c_str());
    tm=clock()-tm;
    system("taskkill /f /im wscript.exe 1>nul 2>nul"),system("taskkill /f /im cscript.exe 1>nul 2>nul");
    printf("\n%s: \n",username.c_str());
    if(tm>TimeLimit) return COLOR_PRINT(0x7,0x1,"Time Limit Exceeded"),printf("! Time used %.0lf ms.\n",tm),1;
    else if(ls>0xc0000000u) return COLOR_PRINT(0x7,0x5,"Runtime Error"),printf("! Time was unavailable!\n"),1;//RE:3221225725
    else if(ls) COLOR_PRINT(0x7,0x0,"Unknown Error!\n"),system("pause");
    else{
        unsigned int rs=system((checker+".exe").c_str());
        if(rs>0xc0000000u) COLOR_PRINT(0x7,0x5,"Checker Runtime Error"),printf("! Time was unavailable!\n"),system("pause");
        else if(!rs) COLOR_PRINT(0x7,0xA,"Accepted"),printf("! Time used %.0lf ms.\n",tm);
        else if(rs==1) return COLOR_PRINT(0x7,0x4,"Wrong Answer"),printf("! Time used %.0lf ms.\n",tm),1;
        else COLOR_PRINT(0x7,0x0,"Unknown Error!\n"),system("pause");
    }
    return 0;
}
void comp(const string&filename){system(("start /B \"g++ compiler\" \""+g__+"\" "+filename+".cpp -o "+filename+".exe "+cp).c_str());}
std::vector<string> user;
int main(){
	if(!fileExists("src.txt")) return puts("Can\' t find file src.txt!"),0;
	puts("Read...");
	std::ifstream fin("src.txt");
	for(string us;!fin.eof();)getline(fin,us),user.push_back(us);
	fin.close();
    system("del /Q data 1>nul 2>nul");
	system("md data 1>nul 2>nul");
	puts("Compiling...");
	if(!fileExists(maker+".cpp")) return puts(("Can\' t find file "+maker+".cpp!").c_str()),0;
	system(("del /Q "+maker+".exe 1>nul 2>nul").c_str()),comp(maker);
	if(!fileExists(checker+".cpp")) return puts(("Can\' t find file "+checker+".cpp!").c_str()),0;
	system(("del /Q "+checker+".exe 1>nul 2>nul").c_str()),comp(checker);
	if(!fileExists(mystd+".cpp")) return puts(("Can\' t find file "+mystd+".cpp!").c_str()),0;
	system(("del /Q "+mystd+".exe 1>nul 2>nul").c_str()),comp(mystd);
	for(const string&us:user){
		if(!fileExists("src\\"+us+".cpp")) return printf("Can\' t find file src\\%s.cpp!\n",us.c_str()),0;
		string sys="del /Q src\\"+us+".exe 1>nul 2>nul";
		system(sys.c_str());
		comp("src\\"+us);
		Sleep(500);
	}
	while(system("wmic process where (name=\"g++.exe\") get ProcessId 2>nul | findstr /r \"[0-9]\" >nul"));
	int maxc;
	printf("Enter the total number of pairs of shots you wish to obtain:");
	scanf("%d",&maxc);//输入你想要的数据组数。
	char sys_make[100],sys_mystd[100];
	strcpy(sys_make,(maker+".exe > data.in").c_str()),strcpy(sys_mystd,(mystd+".exe < data.in > data.ans").c_str());
	for(int t=1,c=1;c<=maxc;t++){
		system(sys_make);system(sys_mystd);
		bool sd=0;
		std::printf("Test %d:\n",t);
		for(const string&us:user)sd|=runcode(us);
		if(sd) SaveData(c);
		putchar('\n');
	}
	return 0;
}

标签:std,const,int,system,return,Judge,编写,include,Special
From: https://www.cnblogs.com/Chen-Jinhui/p/17976205/About-Special-Judge

相关文章

  • 【浏览器扩展】编写Firefox和Chrome的扩展程序
    官方文档Firefox(1)文档https://developer.mozilla.org/zh-CN/docs/Mozilla/Add-ons/WebExtensions(2)文档示例代码https://github.com/mdn/webextensions-examplesChrome(1)文档https://developer.chrome.com/docs/extensions(2)文档示例代码https://github.com/Google......
  • 如何编写一个好的测试用例?才能防止背黑锅
    五星上将麦克阿瑟曾经说过“老夫干测试,测试用例不过删用例!“一让我们来讲一个故事今天和女朋友吵架了,(假设你有女朋友)。今晚又是一个人睡沙发,这天晚上,你躺在沙发上,夜不能寐决定学习一下这个事情——如何编写好的测试用例?测试用例什么是测试用例?在这之前,思考一个问题,下面这个简单的QQ......
  • Adobe InCopy 2024 v19.1 (macOS, Windows) - 编写和副本编辑软件
    AdobeInCopy2024v19.1(macOS,Windows)-编写和副本编辑软件Acrobat、AfterEffects、Animate、Audition、Bridge、CharacterAnimator、Dimension、Dreamweaver、Illustrator、InCopy、InDesign、LightroomClassic、MediaEncoder、Photoshop、PremierePro、AdobeXD......
  • Google的Jax框架的JAX-Triton目前只能成功运行在TPU设备上(使用Pallas为jax编写kernel
    使用Pallas为jax编写kernel扩展,需要使用JAX-Triton扩展包。由于Google的深度学习框架Jax主要是面向自己的TPU进行开发的,虽然也同时支持NVIDIA的GPU,但是支持力度有限,目前JAX-Triton只能在TPU设备上正常运行,无法保证在GPU上正常运行。该结果使用kaggle上的TPU和GPU进行测试获得。......
  • 编写可读代码的艺术
    写让人理解的代码代码的写法应该使理解代码的人所需要的时间最小化变量名使用专业的词避免使用空泛的词给变量名带上附加信息为作用域更大的变量起一个长的名字有目的的使用大小写和下划线让人不会误解的名字不会误解的名字是最好的名字————阅读代码的人应该理解你......
  • Woodpecker CI 设计分析|一个 Go 编写的开源持续集成引擎
    一、前言大家好,这里是白泽。随着Go语言在云原生领域大放异彩,开发者逐渐将目光转移到了这门语言上,而容器则是云原生时代最核心的载体。《WoodpeckerCI设计分析》系列文章将分析开源CI引擎Woodpecker的架构设计,探究Go协程是如何支持由Workflow定义的大量Task的频繁......
  • 42 干货系列从零用Rust编写负载均衡及代理,wmproxy中配置tcp转websocket
    wmproxywmproxy已用Rust实现http/https代理,socks5代理,反向代理,静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket代理等,会将实现过程分享出来,感兴趣的可以一起造个轮子项目地址国内:https://gitee.com/tickbh/wmproxygithub:https://github.com/......
  • 22. 从零用Rust编写正反向代理,一个数据包的神奇HTTP历险记!
    wmproxywmproxy已用Rust实现http/https代理,socks5代理,反向代理,静态文件服务器,四层TCP/UDP转发,内网穿透,后续将实现websocket代理等,会将实现过程分享出来,感兴趣的可以一起造个轮子项目地址国内:https://gitee.com/tickbh/wmproxygithub:https://github.com/tickbh/wmproxy数......
  • 23. 从零用Rust编写正反向代理,流控小姐姐的温柔一刀!
    wmproxywmproxy已用Rust实现http/https代理,socks5代理,反向代理,静态文件服务器,四层TCP/UDP转发,内网穿透,后续将实现websocket代理等,会将实现过程分享出来,感兴趣的可以一起造个轮子项目地址国内:https://gitee.com/tickbh/wmproxygithub:https://github.com/tickbh/wmproxy温......
  • 使用Go语言编写HTTP代理服务器
    在Go语言中,编写一个HTTP代理服务器相对简单且直观。代理服务器的主要职责是接收客户端的请求,然后将请求转发到目标服务器,再将目标服务器的响应返回给客户端。下面是一个简单的示例,展示如何使用Go语言编写一个基本的HTTP代理服务器:go复制代码package mainimport ("io" "log" "......