Skip to content

Advanced Usage

Dark Litss edited this page Jan 26, 2021 · 6 revisions

本文将会介绍一些本插件的高级用法。

修改目录解析格式

OJ-Assistant 是根据当前打开文件的路径来判断这是 OJ 中的哪道题的。

还记得之前的那个 .ojassistconfig.json 吗?

在这个文件中有一行文字叫 pathParseRule

下面跟着的两行就是 OJ-Assistant 解析比赛和普通题目的规则。

解析比赛题目的叫 contestProblem, 解析普通题目的叫 normalProblem

而里面的外星语言是一种被称为 正则表达式 的东西,

它会告诉 OJ-Assistant 如何从一段字符串里提取出咱想要的信息。

先来举个栗子

假设你的 VSCode 打开的目录在 D:\acm, 然后里面又有 ac 文件夹, 里面又有别的文件。

当你打开 a 里面的 1000.cpp 时, 对于 OJ-Assistant 来说,它看见的是这样的字符串:

file:///D:/acm/a/1000.cpp

在前面一节,我们约定好在 a 里存放普通题目的源代码,

1000.cpp题号1000 这道题的源码, cpp 说明应该用 G++ 语言 来提交。

如果你要打开别的题目,那么最多也就题号和语言不一样,其他都差不多。

这样的话,我们只要能把字符串里的那串数字和后缀名提取出来就好了!

也就是: a/(题号).(后缀名)

题号是一串数字,后缀名是几个字母。

正则表达式 中, 我们可以用 \w 来表示一个可以被看见的字符(看不见的字符就是空格、TAB这种看不见,但实际存在的字符)。

+ 可以表示匹配多次, . 可以表示匹配任何字符, $表示字符串的尾部。 () 则是把括号里匹配到的东西提交给 OJ-Assistant。

所以我们可以这样写: a/(\w+)\.(\w+)$

我们在 . 前面加了一个 \ 表示转义,让 . 失去匹配的功能,仅代表一个普通的.

如果我们要在 .ojassistconfig.json 中这样写的话,大概是会出错的。 因为\ 是转义符号, \w 可能又会代表别的意思了。

所以我们要给 \ 再加一个 \, 让它变成 \\, 所以 \w 变成了 \\w\. 变成了 \\.

你可以在这里测试您输入的正则表达式是否正确。

自动数据代入

只要合理搭配现有的功能,你就可以实现绝赞的自动数据代入功能,从此解放你的剪贴板!

OJ-Assistant 提供了 ojassist.dataFile 命令来告诉 VSCode 当前打开文件对应数据文件的路径。

你可以把它写在你的 launch.json 里面, 把它传递到你的程序,或者调试工具里。

a. 基于 shell 的代入方法

如果你的 VSCode 运行在 Linux 环境下,可以试试这样的配置:

点我查看喔~

.vscode/launch.json:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        { // 这段是 Java 的
            "type": "java",
            "name": "java - Launch and Attach Current File",
            "request": "attach",
            "hostName": "localhost",
            "port": 5006,
            "preLaunchTask": "Java: active current file",
            "postDebugTask": "Java: javac clean up"
        },
        {
            "name": "g++ - Build and debug active file",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}/${fileBasenameNoExtension}",
            "args": ["-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}", "<",  "${command:ojassist.dataFile}"],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "C/C++: g++ build active file",
            "miDebuggerPath": "/usr/bin/gdb"
        }
    ]
}

其中,起到作用的一行是:

            "args": ["-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}", "<",  "${command:ojassist.dataFile}"],

它会让 shell 从数据文件里把数据通过管道传递给测试程序的标准输入。

.vscode/tasks.json:

{
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: g++ build active file",
            "command": "/usr/bin/g++",
            "args": [
                "-g",
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}"
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "Task generated by Debugger."
        },
        { // 这段开始是 Java 的
            "type": "shell",
            "label": "Java: Rename source file before compile",
            "command": "cp",
            "args": [
                "${file}",
                "Main.java"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "Compile the java file."
        },
        {
            "type": "shell",
            "label": "Java: javac clean up",
            "command": "rm",
            "args": [
                "Main.class",
                "Main.java"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "presentation": {
                "echo": false,
                "reveal": "silent",
                "clear": false
            },
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "Compile the java file."
        },
        {
            "type": "process",
            "label": "Java: javac build current file",
            "command": "/usr/bin/javac",
            "args": [
                "Main.java"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "presentation": {
                "echo": false,
                "reveal": "silent",
                "clear": true
            },
            "dependsOn": [
                "Java: Rename source file before compile"
            ],
            "detail": "Compile the java file."
        },
        {
            "label": "Java: active current file",
            "type": "shell",
            "command": "java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5006 Main < ${command:ojassist.dataFile}",
            "options": {
                "cwd": "${fileDirname}"
            },
            "isBackground": true,
            "dependsOn": [
                "Java: javac build current file"
            ],
            "presentation": {
                "echo": true,
                "reveal": "always",
                "clear": true,
            },
            "problemMatcher": [
                {
                    "pattern": [
                        {
                            "regexp": "\\b\\B",
                            "file": 1,
                            "location": 2,
                            "message": 3
                        }
                    ],
                    "background": {
                        "activeOnStart": true,
                        "beginsPattern": "^.*Listening for",
                        "endsPattern": "^.*transport dt_socket at address.*"
                    }
                }
            ]
        } // Java 的到此为止
    ],
    "version": "2.0.0"
}

其中,起到作用的一行是:

            "command": "java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5006 Main < ${command:ojassist.dataFile}",

做的是一样的事情,但对的是 Java。

Tip: 这份示例基于 Ubuntu 系统,支持 Java 和 G++。您可能需要修改其中 gdb, g++ 和 javac 的位置。

Tip2: 如果您不需要 Java ,可以把与 Java有关的代码删掉。

b. 基于宏定义的代入方法

对于 Windows 用户来说,上面的方法似乎不是很能正常使用。

但是没关系,问题总比办法多嘛!

许多编译器,可以在编译的过程中传递特定的参数来改变程序在运行过程中的表现。

对于 G++

在我们的代码中,可以使用 #ifdef 宏的名称#endif 来让我们的程序执行一些只有遇到特定宏的情况才会执行的代码。

所以我们可以定义一个叫 OJ_ASSISTANT_DATA_INPUT 的宏,存放数据文件的路径,然后在程序运行的时候通过 freopen 来实现数据重定向就可以了。

.vscode/tasks.json 中,找到 "type": "cppbuild", 的那一段配置,

"args": [ 下紧接一行: "-DOJ_ASSISTANT_DATA_INPUT=${command:ojassist.dataFile}",

整体上来看像这样:

.vscode/tasks.json:

{
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: g++ build active file",
            "command": "/usr/bin/g++",
            "args": [
                "-DOJ_ASSISTANT_DATA_INPUT=${command:ojassist.dataFile}", // 这是你刚刚加的
                "-g",
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}"
            ],
            "options": {
                "cwd": "/usr/bin"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "Generated task by Debugger"
        }
    ],
    "version": "2.0.0"
}

当然,千万别直接复制我的,不然肯定不能用。

然后,你可以在你的程序可以改成:

#include <stdio.h>
int main()
{
    #ifdef OJ_ASSISTANT_DATA_INPUT
    freopen("OJ_ASSISTANT_DATA_INPUT", "r", stdin); // 从 OJ-Assistant 读取输入数据
    #endif
    // 好了,该干啥干啥
    #ifdef OJ_ASSISTANT_DATA_INPUT
    getchar(); // 按任意键退出,你也可以 system("pause");
    #endif
}

接下来,你按 F5 运行的时候,它就可以自动代入数据了。

这段代码只会在你本地调试的时候被执行,提交到 OJ 上之后会被编译器自动忽略。

对于 Java 和其他编程语言

Java 中虽然没有宏的概念,但咱们在执行 Java 程序之前也有类似的操作。

我们知道,在命令行启动一个 Java 程序的方法是 java -jar xxx.jar 或者 java 类名

其实我们还可以加上一个参数 -DOJ_ASSISTANT_DATA_INPUT=${command:ojassist.dataFile},来传递属性。

在程序中,我们可以通过

System.getProperty("OJ_ASSISTANT_DATA_INPUT");

来判断并读取它。

因为 System.in文件输入流 都是流,所以 Scanner 类也可以从一个文件里读取数据。

是不是很简单,自己试着写一个吧!