【譯】Spring 6 入參數據校驗: 綜合指南

一、前言

在 Spring 6.1 中,有一個非常值得注意的重要改進——編程式驗證器實現。Spring 長期以來一直通過注解支持聲明式驗證,而 Spring 6.1 則通過提供專用的編程式驗證方法引入了這一強大的增強功能。

編程式驗證允許開發人員對驗證過程進行細粒度控制,實現動態和有條件的驗證場景,超越了聲明式方法的能力。在本教程中,我們將深入探討實現編程式驗證并將其與 Spring MVC 控制器無縫集成的細節。

二、聲明式驗證與編程式驗證的區別

對于數據驗證,Spring 框架有兩種主要方法:聲明式驗證和編程式驗證。

"聲明式驗證(Declarative validation)"通過域對象上的元數據或注解指定驗證規則。Spring 利用 JavaBean Validation (JSR 380) 注釋(如 @NotNull、@Size 和 @Pattern)在類定義中直接聲明驗證約束。

Spring 會在數據綁定過程中自動觸發驗證(例如,在 Spring MVC 表單提交過程中)。開發人員無需在代碼中明確調用驗證邏輯。

public class User {@NotNullprivate String username;@Size(min = 6, max = 20)private String password;// ...
}

另一方面,“編程式驗證(Programmatic validation)” 在代碼中編寫自定義驗證邏輯,通常使用 Spring 提供的 Validator 接口。這種方法可以實現更動態、更復雜的驗證場景。

開發人員負責顯式調用驗證邏輯,通常在服務層或控制器中進行。

public class UserValidator implements Validator {@Overridepublic boolean supports(Class<?> clazz) {return User.class.isAssignableFrom(clazz);}@Overridepublic void validate(Object target, Errors errors) {User user = (User) target;// 自定義驗證邏輯, 可以讀取多個字段進行混合校驗,編程的方式靈活性大大增加}
}

三、何時使用程序化驗證

在聲明式驗證和編程式驗證之間做出選擇取決于用例的具體要求。

聲明式驗證通常適用于比較簡單的場景,驗證規則可以通過注釋清晰地表達出來。聲明式驗證很方便,也符合慣例,即重于配置的原則。

程序化驗證提供了更大的靈活性和控制力,適用于超出聲明式表達范圍的復雜驗證場景。當驗證邏輯取決于動態條件或涉及多個字段之間的交互時,程序化驗證尤其有用。

我們可以將這兩種方法結合起來使用。我們可以利用聲明式驗證的簡潔性來處理常見的情況,而在面對更復雜的要求時,則采用編程式驗證。

四、程序化驗證器 API 簡介

Spring 中的編程式驗證器 API 的核心是允許創建自定義驗證器類,并定義僅靠注解可能無法輕松捕獲的驗證規則。

以下是創建自定義驗證器對象的一般步驟。

  • 創建一個實現 org.springframework.validation.Validator 接口的類。
  • 重載 supports() 方法,以指定該驗證器支持哪些類。
  • 實現 validate()validateObject() 方法,以定義實際的驗證邏輯。
  • 使用 ValidationUtils.rejectIfEmpty()ValidationUtils.rejectIfEmptyOrWhitespace() 方法,以給定的錯誤代碼拒絕給定字段。
  • 我們可以直接調用 Errors.rejectValue() 方法來添加其他類型的錯誤。
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;@Component
public class UserValidator implements Validator {@Overridepublic boolean supports(Class<?> clazz) {return User.class.isAssignableFrom(clazz);}@Overridepublic void validate(Object target, Errors errors) {User user = (User) target;// 例如: 校驗 username 不能為空ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "field.required", "Username must not be empty.");// 添加更多的自定義驗證邏輯}
}

要使用自定義驗證器,我們可以將其注入 @Controller 或 @Service 等 Spring 組件,或者直接將其實例化。然后,我們調用驗證方法,傳遞要驗證的對象和 Errors 對象以收集驗證錯誤。

public class UserService {private Validator userValidator;public UserService(Validator userValidator) {this.userValidator = userValidator;}public void someServiceMethod(User user) {Errors errors = new BeanPropertyBindingResult(user, "user");userValidator.validate(user, errors);if (errors.hasErrors()) {// 處理數據校驗錯誤}}
}

五、初始化安裝

5.1. Maven 配置

要使用編程式驗證器,我們需要 Spring Framework 6.1 或 Spring Boot 3.2,因為這些是最低支持的版本。

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/>
</parent>

5.2. 領域對象

本教程的領域對象是雇員(Employee) 和部門(Department)對象。我們不會創建復雜的結構,因此可以專注于核心概念。

Employee.java

package demo.customValidator.model;@Data
@Builder
public class Employee {Long id;String firstName;String lastName;String email;boolean active;Department department;
}

Department.java

package demo.customValidator.model;@Data
@Builder
public class Department {Long id;String name;boolean active;
}

六、 實現程序化驗證器

以下 EmployeeValidator 類實現了 org.springframework.validation.Validator 接口并實現了必要的方法。它將根據需要在 Employee 字段中添加驗證規則。

package demo.customValidator.validator;import demo.customValidator.model.Employee;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;public class EmployeeValidator implements Validator {@Overridepublic boolean supports(Class<?> clazz) {return Employee.class.isAssignableFrom(clazz);}@Overridepublic void validate(Object target, Errors errors) {ValidationUtils.rejectIfEmpty(errors, "id", ValidationErrorCodes.ERROR_CODE_EMPTY);ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "First name cannot be empty");ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "Last name cannot be empty");ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "Email cannot be empty");Employee employee = (Employee) target;if (employee.getFirstName() != null && employee.getFirstName().length() < 3) {errors.rejectValue("firstName", "First name must be greater than 2 characters");}if (employee.getLastName() != null && employee.getLastName().length() < 3) {errors.rejectValue("lastName", "Last name must be greater than 3 characters");}}
}

同樣,我們為 Department 類定義了驗證器。如有必要,您可以添加更復雜的驗證規則。

package demo.customValidator.model.validation;import demo.customValidator.model.Department;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;public class DepartmentValidator implements Validator {@Overridepublic boolean supports(Class<?> clazz) {return Department.class.equals(clazz);}@Overridepublic void validate(Object target, Errors errors) {ValidationUtils.rejectIfEmpty(errors, "id", ValidationErrorCodes.ERROR_CODE_EMPTY);ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "Department name cannot be empty");Department department = (Department) target;if(department.getName() != null && department.getName().length() < 3) {errors.rejectValue("name", "Department name must be greater than 3 characters");}}
}

現在我們可以驗證 EmployeeDepartment 對象的實例,如下所示:

Employee employee = Employee.builder().id(2L).build();
//Aurowire if needed
EmployeeValidator employeeValidator = new EmployeeValidator();Errors errors = new BeanPropertyBindingResult(employee, "employee");
employeeValidator.validate(employee, errors);if (!errors.hasErrors()) {System.out.println("Object is valid");
} else {for (FieldError error : errors.getFieldErrors()) {System.out.println(error.getCode());}
}

程序輸出:

First name cannot be empty
Last name cannot be empty
Email cannot be empty

Department 對象也可以進行類似的驗證。

七、鏈式多個驗證器

在上述自定義驗證器中,如果我們驗證了雇員對象,那么 API 將不會驗證部門對象。理想情況下,在驗證特定對象時,應針對所有關聯對象執行驗證。

程序化驗證 API 允許調用其他驗證器,匯總所有錯誤,最后返回結果。使用 ValidationUtils.invokeValidator() 方法可以實現這一功能,如下所示:

public class EmployeeValidator implements Validator {DepartmentValidator departmentValidator;public EmployeeValidator(DepartmentValidator departmentValidator) {if (departmentValidator == null) {throw new IllegalArgumentException("The supplied Validator is null.");}if (!departmentValidator.supports(Department.class)) {throw new IllegalArgumentException("The supplied Validator must support the Department instances.");}this.departmentValidator = departmentValidator;}@Overridepublic void validate(Object target, Errors errors) {//...try {errors.pushNestedPath("department");ValidationUtils.invokeValidator(this.departmentValidator, employee.getDepartment(), errors);} finally {errors.popNestedPath();}}
}
  • pushNestedPath() 方法允許為子對象設置臨時嵌套路徑。在上例中,當對部門對象進行驗證時,路徑被設置為 employee.department
  • 在調用 pushNestedPath() 方法之前,popNestedPath() 方法會將路徑重置為原始路徑。在上例中,它再次將路徑重置為 employee

現在,當我們驗證 Employee 對象時,也可以看到 Department 對象的驗證錯誤。

Department department = Department.builder().id(1L).build();
Employee employee = Employee.builder().id(2L).department(department).build();EmployeeValidator employeeValidator = new EmployeeValidator(new DepartmentValidator());Errors errors = new BeanPropertyBindingResult(employee, "employee");
employeeValidator.validate(employee, errors);if (!errors.hasErrors()) {System.out.println("Object is valid");
} else {for (FieldError error : errors.getFieldErrors()) {System.out.println(error.getField());System.out.println(error.getCode());}
}

程序輸出:

firstName
First name cannot be emptylastName
Last name cannot be emptyemail
Email cannot be emptydepartment.name
Department name cannot be empty

注意打印出來的字段名稱是 department.name。由于使用了 pushNestedPath() 方法,所以添加了 department. 前綴。

八、使用帶消息解析功能的 MessageSource

使用硬編碼的消息并不是一個好主意,因此我們可以將消息添加到資源文件(如 messages.properties)中,然后使用 MessageSource.getMessage() 將消息解析為所需的本地語言,從而進一步改進此代碼。

例如,讓我們在資源文件中添加以下消息:

error.field.empty={0} cannot be empty
error.field.size={0} must be between 3 and 20

為了統一訪問,請在常量文件中添加以下代碼。請注意,這些錯誤代碼是在自定義驗證器實現中添加的。

public class ValidationErrorCodes {public static String ERROR_CODE_EMPTY = "error.field.empty";public static String ERROR_CODE_SIZE = "error.field.size";
}

現在,當我們解析信息時,就會得到屬性文件的信息。

MessageSource messageSource;//...if (!errors.hasErrors()) {System.out.println("Object is valid");
} else {for (FieldError error : errors.getFieldErrors()) {System.out.println(error.getCode());System.out.println(messageSource.getMessage(error.getCode(), new Object[]{error.getField()}, Locale.ENGLISH));}
}

程序輸出:

error.field.empty
firstName cannot be emptyerror.field.empty
lastName cannot be emptyerror.field.empty
email cannot be emptyerror.field.empty
department.name cannot be empty

九、將編程式驗證器與 Spring MVC/WebFlux 控制器集成

將編程式驗證器與 Spring MVC 控制器集成,包括將編程式驗證器注入控制器、在 Spring 上下文中配置它們,以及利用 @Valid 和 BindingResult 等注解簡化驗證。

令人欣慰的是,這種集成還能解決 Ajax 表單提交和控制器單元測試問題。

下面是一個使用我們在前面章節中創建的 EmployeeValidator 對象的 Spring MVC 控制器的簡化示例。

import demo.app.customValidator.model.Employee;
import demo.app.customValidator.model.validation.DepartmentValidator;
import demo.app.customValidator.model.validation.EmployeeValidator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;@Controller
@RequestMapping("/employees")
public class EmployeeController {@InitBinderprotected void initBinder(WebDataBinder binder) {// 注入 編程式驗證器binder.setValidator(new EmployeeValidator(new DepartmentValidator()));}@GetMapping("/registration")public String showRegistrationForm(Model model) {model.addAttribute("employee", Employee.builder().build());return "employee-registration-form";}@PostMapping("/processRegistration")public String processRegistration(@Validated @ModelAttribute("employee") Employee employee,BindingResult bindingResult) {if (bindingResult.hasErrors()) {return "employee-registration-form";}// 處理成功通過數據校驗后表單的邏輯// 通常涉及數據庫操作、身份驗證等。return "employee-registration-confirmation"; // 重定向至成功頁面}
}

之后,當表單提交時,可以使用 ${#fields.hasErrors('*')} 表達式在視圖中顯示驗證錯誤。

在下面的示例中,我們在兩個地方顯示驗證錯誤,即在表單頂部的列表中顯示所有錯誤,然后顯示單個字段的錯誤。請根據自己的要求定制代碼。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Employee Registration</title>
</head>
<body><h2>Employee Registration Form</h2><!-- Employee Registration Form -->
<form action="./processRegistration" method="post" th:object="${employee}"><!-- Display validation errors, if any --><div th:if="${#fields.hasErrors('*')}"><div style="color: red;"><p th:each="error : ${#fields.errors('*')}" th:text="${error}"></p></div></div><!-- Employee ID (assuming it's a hidden field for registration) --><input type="hidden" th:field="*{id}" /><!-- Employee First Name --><label for="firstName">First Name:</label><input type="text" id="firstName" th:field="*{firstName}" required /><span th:if="${#fields.hasErrors('firstName')}" th:text="#{error.field.size}"></span><br/><!-- Employee Last Name --><label for="lastName">Last Name:</label><input type="text" id="lastName" th:field="*{lastName}" required /><span th:if="${#fields.hasErrors('lastName')}" th:text="#{error.field.size}"></span><br/><!-- Employee Email --><label for="email">Email:</label><input type="email" id="email" th:field="*{email}" required /><span th:if="${#fields.hasErrors('email')}" th:text="#{error.field.size}"></span><br/><!-- Employee Active Status --><label for="active">Active:</label><input type="checkbox" id="active" th:field="*{active}" /><br/><!-- Department Information --><h3>Department:</h3><label for="department.name">Department Name:</label><input type="text" id="department.name" th:field="*{department.name}" required /><span th:if="${#fields.hasErrors('department.name')}" th:text="#{error.field.size}"></span><br/><!-- Submit Button --><button type="submit">Register</button></form></body>
</html>

當我們運行應用程序并提交無效表單時,會出現如圖所示的錯誤:

在這里插入圖片描述

十、單元測試編程式驗證器

我們可以將自定義驗證器作為模擬依賴關系或單個測試對象進行測試。下面的 JUnit 測試用例將測試 EmployeeValidator

我們編寫了兩個非常簡單的基本測試供快速參考,您也可以根據自己的需求編寫更多測試。

import demo.app.customValidator.model.Department;
import demo.app.customValidator.model.Employee;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;public class TestEmployeeValidator {static EmployeeValidator employeeValidator;@BeforeAllstatic void setup() {employeeValidator = new EmployeeValidator(new DepartmentValidator());}@Testvoid validate_ValidInput_NoErrors() {// Set up a valid userEmployee employee = Employee.builder().id(1L).firstName("Lokesh").lastName("Gupta").email("admin@howtodoinjava.com").department(Department.builder().id(2L).name("Finance").build()).build();Errors errors = new BeanPropertyBindingResult(employee, "employee");employeeValidator.validate(employee, errors);Assertions.assertFalse(errors.hasErrors());}@Testvoid validate_InvalidInput_HasErrors() {// Set up a valid userEmployee employee = Employee.builder().id(1L).firstName("A").lastName("B").email("C").department(Department.builder().id(2L).name("HR").build()).build();Errors errors = new BeanPropertyBindingResult(employee, "employee");employeeValidator.validate(employee, errors);Assertions.assertTrue(errors.hasErrors());Assertions.assertEquals(3, errors.getErrorCount());}
}

最佳做法是確保測試涵蓋邊緣情況和邊界條件。這包括輸入處于允許的最小值或最大值的情況。

十一、結論

在本教程中,我們通過示例探討了 Spring 6.1 Programmatic Validator API 及其實施指南。程序化驗證允許開發人員對驗證過程進行細粒度控制。

我們討論了如何創建和使用自定義驗證器類,并將其與 Spring MVC 控制器集成。我們學習了如何使用消息解析,隨后還討論了如何測試這些驗證器以實現更強大的編碼實踐。

代碼地址:programmatic-validator

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/166681.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/166681.shtml
英文地址,請注明出處:http://en.pswp.cn/news/166681.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網站定制開發有哪些分類?|企業軟件app小程序定制

網站定制開發有哪些分類&#xff1f;|企業軟件app小程序定制 網站定制開發是指根據客戶需求&#xff0c;為其量身定制設計和開發的網站服務。目前&#xff0c;網站定制開發主要分為以下幾個分類&#xff1a; 1. 靜態網站定制開發&#xff1a;靜態網站是由HTML、CSS和JavaScrip…

手寫promise(3)-- 實例方法 靜態方法

目錄 實例方法 catch finally 靜態方法 reslove reject race all allSettled any 實例方法 提供給promise實例的方法 包括catch 與finally catch Promise 實例的 catch() 方法用于注冊一個在 promise 被拒絕時調用的函數。它會立即返回一個等效的 Promise 對象&…

一文詳解 requests 庫中 json 參數和 data 參數的用法

在requests庫當中&#xff0c;requests請求方法&#xff0c;當發送post/put/delete等帶有請求體的請求時&#xff0c;有json和data2個參數可選。 眾所周知&#xff0c;http請求的請求體格式主要有以下4種&#xff1a; application/json applicaiton/x-www-from-urlencoded …

java堆文件排查

技術主題 在之前的開發的一個項目中&#xff0c;因為程序的一個bug&#xff0c;導致一些引用的對象一直沒有回收&#xff0c;從而導致堆內存一直在增大&#xff0c;老年代一直在增大&#xff0c;老年代進行堆積&#xff0c;后來的排查思路是通過dump堆的文件&#xff0c;然后對…

Dockerfile-CentOS7.9+Python3.11.2

本文為CentOS7.9下安裝Python3.11.2環境的Dockerfile # CentOS with Python3.11.2 # Author xxmail.com# build a new image with basic centos FROM centos:centos7.9.2009 # who is the author MAINTAINER xxmail.comRUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/…

vue的生命周期及不同階段狀態可以進行的行為

什么是vue的生命周期&#xff1f; Vue 的實例從創建到銷毀的過程 &#xff0c;就是生命周期 &#xff0c;也就是從開始創建 &#xff0c;初始化數據 &#xff0c;編譯模板 &#xff0c;掛載Dom到渲染DOM &#xff0c;更新數據再到渲染 &#xff0c;卸載等一系列的過程 &#x…

OpenAI研發神秘“Q*”模型:科學家認輸,AI贏了人類關鍵一戰

圖片來源&#xff1a;視覺中國 作者丨葉蓁 編輯丨康曉 出品丨深網騰訊新聞小滿工作室 在山姆奧特曼&#xff08;Sam Altman&#xff09;被OpenAI前董事會突然罷免之前&#xff0c;數位研究人員向董事會發送了一封信&#xff0c;警告稱他們發現了一種能夠威脅到人類的強大人工…

IIS 基線安全加固操作

目錄 賬號管理、認證授權 ELK-IIS-01-01-01 ELK-IIS-01-01-02 ELK-IIS-01-01-03 ELK-IIS-01-01-04 日志配置 ELK-IIS-02-01-01 ELK-IIS-02-01-02 ??????? ELK-IIS-02-01-03 通信協議 ELK-IIS-03-01-01 設備其他安全要求 ELK-IIS-04-01-01 ??????? ELK-I…

【DDS】OpenDDS配置與使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 這篇文章主要介紹OpenDDS配置與使用。 無專精則不能成&#xff0c;無涉獵則不能通。——梁啟超 歡迎來到我的博客&#xff0c;一起學習&#xff0c;共同進步。 喜歡的朋友可以關注一下&#xff0c;下次更…

華為云編譯構建CodeArts Build常見問答匯總

1.【Build】公有云編譯構建是否支持導入外部機器做執行機 答&#xff1a;參考鏈接&#xff1a;https://support.huaweicloud.com/usermanual-devcloud/devcloud_01_0017.html ? 使用代理機功能&#xff0c;需要配備1臺4U8G或以上規格、磁盤>80GB的主機。 ? 安裝代理的…

Ubuntu 啟用 root 用戶

在啟用 root 用戶之前&#xff0c;我們先來了解一下&#xff0c; ubuntu 命令的組成。 打開 ubuntu 的終端&#xff0c;現在的命令行是由 topeetubuntu:~$ 這幾個字母組成&#xff0c;那么這幾個字母都代表 什么意思呢&#xff1f; topeet …

配電室智慧運維監控系統

配電室智能運維監控系統是一個綜合性的管理系統&#xff0c;專門針對配電室的運維工作進行設計。依托電易云-智慧電力物聯網&#xff0c;它融合了先進的監測技術、自動化技術、數據分析技術等&#xff0c;對配電室進行全方位、實時的智能化監控和管理&#xff0c;以提升配電室運…

人工智能對當代生活的影響

人工智能&#xff08;AI&#xff09;是指通過模擬人類智能的方式&#xff0c;使機器能夠執行某些需要智能的任務。隨著技術的快速發展和應用的廣泛推廣&#xff0c;人工智能已經深入到我們的日常生活中&#xff0c;對我們的生活和社會產生了深遠的影響。本文將探討人工智能對當…

Django同時連接多種數據庫

我的使用場景需要同時連接達夢數據庫和MYSQL數據庫&#xff0c;有的功能需要查詢達夢&#xff0c;有的功能則需要查詢MYSQL。 第一步&#xff1a;在 Django 的 settings.py 文件中&#xff0c;配置多個數據庫連接。你可以在 DATABASES 字典中添加多個數據庫配置。每個數據庫配置…

關于 Google AMP 和 SEO

Google 于 2015 年首次推出 AMP&#xff0c;即加速移動頁面。借助開源 AMP 框架&#xff0c;網頁設計師可以制作快速加載的移動網頁。該框架的創建是為了應對使用移動設備訪問互聯網的個人數量的增加。從那時起&#xff0c;谷歌一直在推動使用 AMP 來增強移動設備上的 SEO 和用…

【漏洞復現】好視通視頻會議系統(fastmeeting) toDownload.do接口存在任意文件讀取漏洞 附POC

漏洞描述 “好視通”是國內云視頻會議知名品牌,擁有多項創新核心技術優勢、多方通信服務牌照及行業全面資質 [5] ,專注為政府、公檢法司、教育、集團企業等用戶提供“云+端+業務全場景”解決方案。用全國產、高清流暢、安全穩定的云視頻服務助力各行各業數字化轉型。 其視頻…

opencv-Meanshift 和 Camshift 算法

MeanShift 和 CamShift 都是用于目標跟蹤的算法&#xff0c;基于顏色直方圖的方法。它們主要用于在視頻序列中追蹤運動的對象。 MeanShift&#xff08;均值漂移&#xff09;: 原理: MeanShift 算法的基本思想是通過不斷調整窗口的中心&#xff0c;使得窗口中的樣本點的平均值向…

介紹正則表達式及其用法

正則表達式&#xff08;Regular Expression&#xff09;&#xff0c;簡稱為"正則"&#xff0c;是一種用于描述、匹配、搜索文本的方式。正則表達式通常由符號和字符組成&#xff0c;可以用于匹配和搜索特定模式的文本。 以下是一些常用的正則表達式元字符及其用法&am…

多模態常見任務介紹

視覺問答&#xff08;VQA&#xff0c; Visual Question Answer&#xff09; 目標&#xff1a;給定一個圖片以及問題&#xff0c;需要理解圖片的內容并基于此用自然語言回答問題。 例如&#xff0c;圖像中發生什么事&#xff0c;人物穿的衣服是什么顏色&#xff0c;圖像中有多…