当前位置:   article > 正文

关于SARD漏洞数据集的预处理_sard数据集

sard数据集

前言 关于SARD

软件保障参考数据集(SARD)包含118种CWE漏洞类型,是专门为评估静态分析工具的能力而开发的。数据集中的每一个样本都以其函数名称为标签,后缀字符串为 “good “或 “bad”,因此我们可以很容易地利用这些后缀信息来生成每个样本的标签。
为了从SARD中构建一个java漏洞数据集,需要对SARD进行一定的预处理。
这里SARD使用Juiletjava。

一、删除文件中的antbuild、web.xml等无用文件和文件夹:

/**
	 * SARD数据集预处理:去除web.xml、antbuild等无用文件和文件夹
	 * @param filePath
	 */
	public static void getFile(String filePath) {
		File file = new File(filePath);
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for (File file1 : files) {
                if(file1.isDirectory()){
                	if("antbuild".equals(file1.getName())){
                		deleteFileOrDirectory(file1.getAbsolutePath());
                	}
                    getFile(file1.getAbsolutePath());
                }else{
                	if("ServletMain.java".equals(file1.getName())){
                		deleteFileOrDirectory(file1.getAbsolutePath());
                	}
                }
            }
        }else{
        	if("ServletMain.java".equals(file.getName())){
        		deleteFileOrDirectory(file.getAbsolutePath());
        	}
        }
	}
	/**
     * 删除文件或文件夹
     *
     * @param fileName 文件名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteFileOrDirectory(String fileName) {
        File file = new File(fileName);  // fileName是路径或者file.getPath()获取的文件路径
        if (file.exists()) {
            if (file.isFile()) {
                return deleteFile(fileName);  // 是文件,调用删除文件的方法
            } else {
                return deleteDirectory(fileName);  // 是文件夹,调用删除文件夹的方法
            }
        } else {
            System.out.println("文件或文件夹删除失败:" + fileName);
            return false;
        }
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        if (file.isFile() && file.exists()) {
            file.delete();
            System.out.println("删除文件成功:" + fileName);
            return true;
        } else {
            System.out.println("删除文件失败:" + fileName);
            return false;
        }
    }

    /**
     * 删除文件夹
     * 删除文件夹需要把包含的文件及文件夹先删除,才能成功
     *
     * @param directory 文件夹名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteDirectory(String directory) {
        // directory不以文件分隔符(/或\)结尾时,自动添加文件分隔符,不同系统下File.separator方法会自动添加相应的分隔符
        if (!directory.endsWith(File.separator)) {
            directory = directory + File.separator;
        }
        File directoryFile = new File(directory);
        // 判断directory对应的文件是否存在,或者是否是一个文件夹
        if (!directoryFile.exists() || !directoryFile.isDirectory()) {
            System.out.println("文件夹删除失败,文件夹不存在" + directory);
            return false;
        }
        boolean flag = true;
        // 删除文件夹下的所有文件和文件夹
        File[] files = directoryFile.listFiles();
        for (int i = 0; i < files.length; i++) {  // 循环删除所有的子文件及子文件夹
            // 删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            } else {  // 删除子文件夹
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
        }

        if (!flag) {
            System.out.println("删除失败");
            return false;
        }
        // 最后删除当前文件夹
        if (directoryFile.delete()) {
            System.out.println("删除成功:" + directory);
            return true;
        } else {
            System.out.println("删除失败:" + directory);
            return false;
        }
    }
  • 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
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113

二、二分类问题数据集的构建

111

三、多分类问题数据集的构建

package Main;

import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

public class JavaParser {
	/**
	 * SARD数据集预处理:去除web.xml、antibuild等无用文件和文件夹
	 * @param filePath
	 */
	public static void getFile(String filePath) {
		File file = new File(filePath);
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for (File file1 : files) {
                if(file1.isDirectory()){
                	if("antbuild".equals(file1.getName())){
                		deleteFileOrDirectory(file1.getAbsolutePath());
                	}
                    getFile(file1.getAbsolutePath());
                }else{
                	if("ServletMain.java".equals(file1.getName())){
                		deleteFileOrDirectory(file1.getAbsolutePath());
                	}
                }
            }
        }else{
        	if("ServletMain.java".equals(file.getName())){
        		deleteFileOrDirectory(file.getAbsolutePath());
        	}
        }
	}
   
	public static void main(String[] args) throws IOException {
		String filePath = "C:\\Users\\rooooot\\Desktop\\SARD-Java\\generality test\\CWE614_Sensitive_Cookie_Without_Secure";
		String targetPath = "C:\\Users\\rooooot\\Desktop\\SARD-Java\\multi-class classification";
		HashMap<String, String> cweMap = new HashMap<String, String>();
    	cweMap.put("CWE643", "xpathi");
    	cweMap.put("CWE614", "securecookie");
    	cweMap.put("CWE327", "crypto");
    	cweMap.put("CWE328", "hash");
    	cweMap.put("CWE90", "ldapi");
    	cweMap.put("CWE89", "sqli");
    	cweMap.put("CWE78", "cmdi");
		GenerateClassFile(filePath,targetPath,cweMap);
  }
    /**
     * 根据CWE编号生成类级Java文件以及xml标签
     * @param filePath
     * @throws IOException 
     */
    public static void GenerateClassFile(String Path,String targetPath,HashMap<String, String> cweMap) throws IOException {
    	File directory = new File(Path);
    	int i = 0;
    	for(File javaFile:directory.listFiles()) {
    		//无漏洞函数缓存表
        	ArrayList<String> goodList = new ArrayList<String>();
        	//有漏洞函数缓存表
        	ArrayList<String> badList = new ArrayList<String>();
        	JavaProjectBuilder builder =  new JavaProjectBuilder();
            //源码目录
            builder.addSourceTree(javaFile);
            //读取CWE编号
            String[] filenames = javaFile.getName().split("_");
            String cwe = filenames[0];
            //读取文件编号
            String fileno = filenames[filenames.length-1].split("\\.")[0];
            //不包含跨文件代码,直接生成
            if(!check(fileno)) {
            	//读取漏洞函数及无漏洞函数
                Collection<JavaClass> classes = builder.getClasses();
                Iterator<JavaClass> it = classes.iterator();
                //逐个类进行读取
                while(it.hasNext()) {
                	JavaClass cls = builder.getClassByName(it.next().getCanonicalName());
                    List<JavaMethod> methods = cls.getMethods();
                    for(JavaMethod method : methods){
                        String methodName = method.getName(); //方法名
                        if(ignoreCaseIndexOf(methodName,"bad")!=-1) {
                        	badList.add(method.getSourceCode());
                        }else if(ignoreCaseIndexOf(methodName,"good")!=-1){
                        	goodList.add(method.getSourceCode());
    					}
                    }
                }
                //生成无漏洞类文件及其xml说明文件
                //重建java类文件
                File f = new File(targetPath+"\\"+javaFile.getName());//新建一个文件对象,如果不存在则创建一个该文件
                FileWriter fw;
                StringBuffer codeFile = new StringBuffer(readFile(javaFile.getAbsolutePath()));
                try {
                	fw = new FileWriter(f);
                	for(String text:badList) {
                		int start = ignoreCaseIndexOf(codeFile.toString(), text);
                		if(start!=-1) {
                			codeFile.replace(start, start+text.length(),"");
                		}else {
    						System.out.println("文件解析异常:"+javaFile.getAbsolutePath());
    					}
                    }
                	fw.write(codeFile.toString());//将字符串写入到指定的路径下的文件中
                	fw.close();
                } catch (IOException e) { 
                	e.printStackTrace(); 
                	}
                String[] args = {javaFile.getName().split("\\\\.")[0],cweMap.get(cwe),Integer.toString(i),"false",cwe.replaceAll("CWE", "")};
                i++;
                createXML(targetPath, args);
                //生成有漏洞类文件及其xml说明文件
                //重建java类文件
                File fv = new File(targetPath+"\\"+"v_"+javaFile.getName());//新建一个文件对象,如果不存在则创建一个该文件
                FileWriter fwv;
                StringBuffer codeFilev = new StringBuffer(readFile(javaFile.getAbsolutePath()));
                try {
                	fwv = new FileWriter(fv);
                	for(String text:goodList) {
                		int start = ignoreCaseIndexOf(codeFilev.toString(), text);
                		if(start!=-1) {
                			codeFilev.replace(start, start+text.length(),"");
                		}else {
    						System.out.println("文件解析异常:"+javaFile.getAbsolutePath());
    					}
                    }
                	fwv.write(codeFilev.toString());//将字符串写入到指定的路径下的文件中
                	fwv.close();
                } catch (IOException e) { 
                	e.printStackTrace(); 
                	}
                String[] argsv = {"v_"+javaFile.getName().split("\\\\.")[0],cweMap.get(cwe),Integer.toString(i),"true",cwe.replaceAll("CWE", "")};
                i++;
                createXML(targetPath, argsv);
            }else {   //包含跨文件代码,暂时忽略
    			
    		}
    	}
    	System.out.println("总文件数:"+i);
    }
    /**
     * 根据CWE编号生成函数片段级Java文件及xml标签
     * @param filePath
     */
    public static void GenerateSliceFile(String filePath) {
    	
    }
    /**
     * 删除文件或文件夹
     * @param fileName 文件名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteFileOrDirectory(String fileName) {
        File file = new File(fileName);  // fileName是路径或者file.getPath()获取的文件路径
        if (file.exists()) {
            if (file.isFile()) {
                return deleteFile(fileName);  // 是文件,调用删除文件的方法
            } else {
                return deleteDirectory(fileName);  // 是文件夹,调用删除文件夹的方法
            }
        } else {
            System.out.println("文件或文件夹删除失败:" + fileName);
            return false;
        }
    }
    /**
     * 删除文件
     *
     * @param fileName 文件名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        if (file.isFile() && file.exists()) {
            file.delete();
            System.out.println("删除文件成功:" + fileName);
            return true;
        } else {
            System.out.println("删除文件失败:" + fileName);
            return false;
        }
    }

    /**
     * 删除文件夹
     * 删除文件夹需要把包含的文件及文件夹先删除,才能成功
     *
     * @param directory 文件夹名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteDirectory(String directory) {
        // directory不以文件分隔符(/或\)结尾时,自动添加文件分隔符,不同系统下File.separator方法会自动添加相应的分隔符
        if (!directory.endsWith(File.separator)) {
            directory = directory + File.separator;
        }
        File directoryFile = new File(directory);
        // 判断directory对应的文件是否存在,或者是否是一个文件夹
        if (!directoryFile.exists() || !directoryFile.isDirectory()) {
            System.out.println("文件夹删除失败,文件夹不存在" + directory);
            return false;
        }
        boolean flag = true;
        // 删除文件夹下的所有文件和文件夹
        File[] files = directoryFile.listFiles();
        for (int i = 0; i < files.length; i++) {  // 循环删除所有的子文件及子文件夹
            // 删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            } else {  // 删除子文件夹
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
        }

        if (!flag) {
            System.out.println("删除失败");
            return false;
        }
        // 最后删除当前文件夹
        if (directoryFile.delete()) {
            System.out.println("删除成功:" + directory);
            return true;
        } else {
            System.out.println("删除失败:" + directory);
            return false;
        }
    }
    /**
     * 根据传入的参数创建XML标签文件
     * @param path:目标文件路径
     * @param args {文件名,category的值,test-number的值,vulnerability的值,cwe编号}
     */
    public static void createXML(String path,String[] args) {
    	// 1.声明文件名称
        String fileName = args[0];
        // 2.创建dom对象
        Document document = DocumentHelper.createDocument();
        // 3.添加节点,根据需求添加,这里我只是设置了一个head节点,下面有name和age两个子节点
        Element test_metadata = document.addElement("test-metadata");
        Element wbe_version = test_metadata.addElement("wbe-version");
        wbe_version.setText("1.1");
        Element category = test_metadata.addElement("category");
        category.addText(args[1]);
        Element test_number = test_metadata.addElement("test-number");
        test_number.addText(args[2]);
        Element vulnerability = test_metadata.addElement("vulnerability");
        vulnerability.addText(args[3]);
        Element cwe = test_metadata.addElement("cwe");
        cwe.addText(args[4]);
        // 4、格式化模板
        //OutputFormat format = OutputFormat.createCompactFormat();
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("UTF-8");
        // 5、生成xml文件
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            XMLWriter writer = new XMLWriter(out, format);
            writer.write(document);
            writer.close();
        } catch (IOException e) {
            System.out.println("生成xml文件失败。文件名【" + fileName + "】");
        }
        // 6、生成的XML利用文件输出流输出到文件
        try (FileOutputStream fos = new FileOutputStream(path+"\\"+fileName + ".xml")) {
            fos.write(out.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 判断字符串中是否包含字母
     * @param str
     * @return
     */
    public static boolean check(String str) {
    	String regex=".*[a-zA-Z]+.*";
    	Matcher m=Pattern.compile(regex).matcher(str);
    	return m.matches();
    }
    /** 
     * 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始,不区分大小。 
     *  
     * @param subject 被查找字符串。 
     * @param search 要查找的子字符串。 
     * @return 指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。 
     */  
    public static int ignoreCaseIndexOf(String subject, String search) {  
        return ignoreCaseIndexOf(subject, search,-1);  
    }  
      
    /** 
     * 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始,不区分大小。 
     *  
     * @param subject 被查找字符串。 
     * @param search 要查找的子字符串。 
     * @param fromIndex 开始查找的索引位置。其值没有限制,如果它为负,则与它为 0 的效果同样:将查找整个字符串。 
     *          如果它大于此字符串的长度,则与它等于此字符串长度的效果相同:返回 -1。 
     * @return 指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。 
     */  
    public static int ignoreCaseIndexOf(String subject, String search, int fromIndex) {  
        //当被查找字符串或查找子字符串为空时,抛出空指针异常。  
        if (subject == null || search == null) {  
            throw new NullPointerException("输入的参数为空");  
        }  
        fromIndex = fromIndex < 0 ? 0 : fromIndex;  
        if (search.equals("")) {  
            return fromIndex >= subject.length() ? subject.length() : fromIndex;  
        }  
        int index1 = fromIndex;  
        int index2 = 0;   
        char c1;  
        char c2;  
        loop1: while (true) {  
            if (index1 < subject.length()) {  
                c1 = subject.charAt(index1);  
                c2 = search.charAt(index2);  
            } else {  
                break loop1;  
            }  
            while (true) {  
                if (isEqual(c1, c2)) {  
                    if (index1 < subject.length() - 1  
                            && index2 < search.length() - 1) {  
                        c1 = subject.charAt(++index1);  
                        c2 = search.charAt(++index2);  
                    } else if (index2 == search.length() - 1) {  
                        return fromIndex;  
                    } else {  
                        break loop1;  
                    }  
                } else {  
                    index2 = 0;  
                    break;  
                }  
            }  
            //重新查找子字符串的位置  
            index1 = ++fromIndex;  
        }  
        return -1;  
    }  
     
    /** 
     * 判断两个字符是否相等。 
     * @param c1 字符1 
     * @param c2 字符2 
     * @return 若是英文字母,不区分大小写,相等true,不等返回false; 
     *          若不是则区分,相等返回true,不等返回false。 
     */  
    private static boolean isEqual(char c1,char c2){  
            //  字母小写                   字母大写  
        if(((97<=c1 && c1<=122) || (65<=c1 && c1<=90))  
                && ((97<=c2 && c2<=122) || (65<=c2 && c2<=90))  
                && ((c1-c2==32) || (c2-c1==32))){  
            return true;  
        }  
        else if(c1==c2){  
            return true;  
        }  
        return false;  
    }  
    /**
     * 将java文件读取成字符串返回
     * @param file
     * @return
     * @throws IOException
     */
    public static String readFile(String file) throws IOException {
    	BufferedReader reader = new BufferedReader(new FileReader (file));
    	String line = null;
    	StringBuilder stringBuilder = new StringBuilder();
    	String ls = System.getProperty("line.separator");
    	try {
    	while((line = reader.readLine()) != null) {
    	stringBuilder.append(line);
    	stringBuilder.append(ls);
    	}
    	return stringBuilder.toString();
    	} finally {
    	reader.close();
    	}
    	}
}

  • 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
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407

2024.06.14更新:
我把我处理好的数据集放在下面,需要的自行下载:

链接:https://pan.baidu.com/s/17cUtYQebqfQwELP43DwUhw?pwd=7rc8 
提取码:7rc8
  • 1
  • 2
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/737948
推荐阅读
相关标签
  

闽ICP备14008679号