零基础鸿蒙应用开发第三十三节:正则表达式基础与应用
【学习目标】
- 掌握正则表达式的核心语法(元字符、量词、边界匹配),能独立构建手机号、密码、日期等常见场景的正则模式。
- 熟练使用ArkTS的
RegExp类实现字符串校验、提取与替换操作,理解test()和exec()的核心差异。 - 实战鸿蒙应用中JSON数据字段的正则校验,整合
JsonUtil工具类完成“读取JSON→反序列化→字段校验”的完整链路。 - 规避正则表达式开发中的常见陷阱(转义字符、贪婪匹配、边界符遗漏)。
- 理解工程化开发规范:将校验逻辑封装为静态工具类,实现代码复用与职责分离。
【学习重点】
- 正则核心语法:元字符(
^/$/\d/[])、量词({n}/{n,m}/+/*)的组合使用,重点掌握手机号/密码/日期的正则构建逻辑。 - ArkTS实战:
RegExp类的test()(格式校验)和exec()(内容提取)方法,全局匹配(g修饰符)的实现方式。 - 工程化落地:将校验逻辑封装为
ValidationUtil静态工具类,结合JsonUtil完成本地JSON文件的字段合法性校验。 - 避坑指南:转义字符双重处理、贪婪匹配优化、鸿蒙预览器/真机运行的环境适配。
一、工程准备
本节我们将创建名为RegexJsonDemo的工程,基于鸿蒙5.0(API12)开发,使用DevEco Studio 6.0+工具,项目结构目录如下:
entry/
├── src/
│ ├── main/
│ │ ├── ets/
│ │ │ ├── model/ // 数据模型层:仅定义类型接口
│ │ │ │ ├── UserInfo.ets // 用户信息字段接口(与JSON文件对齐)
│ │ │ │ └── RegexModel.ets // 正则校验规则的类型约束接口
│ │ │ ├── constant/ // 常量层:存储固定正则规则
│ │ │ │ └── RegexValidator.ets // 手机号/密码/日期等正则常量
│ │ │ ├── utils/ // 工具层:封装可复用方法
│ │ │ │ ├── JsonUtil.ets // 复用JSON读写工具类
│ │ │ │ └── ValidationUtil.ets // 静态工具类:用户信息正则校验
│ │ │ └── pages/ // 页面层:UI渲染+工具调用
│ │ │ └── Index.ets // 主页面:演示正则+JSON校验流程
│ │ └── resources/ // 资源目录
│ │ └── rawfile/ // 本地静态资源
│ │ └── user_info.json // 测试用JSON文件
二、正则表达式核心基础
正则表达式是通过模式字符串匹配字符串的工具,ArkTS完全兼容ECMAScript规范,核心由「元字符+量词+修饰符」组成。
1. 正则创建方式
ArkTS支持两种创建方式,适用于不同场景:
// 演示方法:可在Index.ets中调用测试
testRegexCreate() {
// 方式1:字面量形式(推荐)- 语法简洁、性能高,适用于固定模式
const phoneRegex = /^1[3-9]\d{9}$/;
// 方式2:构造函数形式 - 适用于动态拼接模式,需注意转义符\\
const phonePrefix = '1[3-9]';
const phoneSuffix = '\\d{9}'; // 字符串中\需双重转义
const phoneRegex2 = new RegExp(`^${phonePrefix}${phoneSuffix}$`);
// 测试结果(两者等价)
console.log(`${phoneRegex.test("13800138000")}`); // true
console.log(`${phoneRegex2.test("13800138000")}`); // true
}
2. 核心元字符(必掌握)
元字符定义匹配规则,是正则的基础,常用元字符如下:
| 元字符 | 含义 | 示例 | 匹配结果 |
|---|---|---|---|
. |
匹配任意单个字符(除换行) | a.b |
"acb"、"a2b"、"a@b" |
\d |
匹配数字(0-9) | \d{3} |
"123"、"456" |
\D |
匹配非数字字符 | \D+ |
"abc"、"x_y" |
\w |
匹配字母/数字/下划线 | \w+ |
"user123"、"name_01" |
\W |
匹配非单词字符 | \W |
"@"、"#"、空格 |
^ |
匹配字符串开头 | ^hello |
"hello world" 的开头"hello" |
$ |
匹配字符串结尾 | world$ |
"hello world" 的结尾"world" |
[] |
字符集(匹配任意一个) | [abc] |
"a"、"b"、"c" |
[^] |
否定字符集 | [^0-9] |
非数字字符(如"a"、"@") |
() |
分组捕获 | (\d{4})-(\d{2}) |
捕获"2024-06"中的"2024"和"06" |
3. 量词(控制匹配次数)
量词指定元字符/分组的匹配次数,核心量词如下:
| 量词 | 含义 | 示例 | 匹配结果 |
|---|---|---|---|
{n} |
精确匹配n次 | \d{2} |
"12"、"99" |
{n,} |
至少匹配n次 | a{2,} |
"aa"、"aaa" |
{n,m} |
匹配n-m次 | a{1,3} |
"a"、"aa"、"aaa" |
+ |
匹配1次及以上(等价于{1,}) | \d+ |
"1"、"123" |
* |
匹配0次及以上(贪婪) | a* |
""、"a"、"aaa" |
? |
匹配0次或1次 | https? |
"http"、"https" |
4. 修饰符(修改匹配行为)
修饰符用于调整正则的匹配规则,常用修饰符:
| 修饰符 | 含义 | 示例 | 效果 |
|---|---|---|---|
i |
忽略大小写 | /hello/i |
匹配"Hello"、"HELLO" |
g |
全局匹配 | /\d+/g |
匹配字符串中所有数字组(如"123abc456"中的"123"和"456") |
m |
多行匹配 | /^hello/m |
匹配"hello\nworld"中第一行开头的"hello" |
5. 经典正则实战
案例1:中国大陆手机号
// 规则:11位数字,第一位为1,第二位为3-9,后9位任意数字
testRegexTel() {
const phoneRegex = /^1[3-9]\d{9}$/;
console.log(`${phoneRegex.test("13800138000")}`); // true
console.log(`${phoneRegex.test("12800138000")}`); // false(第二位非法)
console.log(`${phoneRegex.test("138001380")}`); // false(长度不足)
}
案例2:强密码(8-20位,含数字+字母+特殊字符)
testStrongPassword() {
const pwdRegex = /^(?=.*\d)(?=.*[a-zA-Z])(?=.*[!@#$%^&*]).{8,20}$/;
// 测试
console.log(`${pwdRegex.test("Test123!")}`); // true
console.log(`${pwdRegex.test("12345678")}`); // false(无字母/特殊字符)
}
案例3:日期(yyyy-mm-dd,限制月份/日期范围)
testRegexDate() {
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
console.log(`${dateRegex.test("2024-06-01")}`); // true
console.log(`${dateRegex.test("2024/06/01")}`); // false
console.log(`${dateRegex.test("2024-6-1")}`); // false
}
三、ArkTS中RegExp类的核心方法
RegExp类提供两个核心方法,覆盖绝大多数字符串处理场景:
1. test():格式校验(返回布尔值)
用于快速判断字符串是否符合正则规则,适用于表单校验、字段合法性检查:
testRegexTest() {
const emailRegex = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
const emailList = ["user@example.com", "invalid-email", "user@.com"];
emailList.forEach((email, idx) => {
console.log(`邮箱${idx+1} [${email}]:${emailRegex.test(email)}`);
});
// 输出:
// 邮箱1 [user@example.com]:true
// 邮箱2 [invalid-email]:false
// 邮箱3 [user@.com]:false
}
2. exec():内容提取(返回匹配数组/null)
用于提取字符串中符合规则的内容,结合g修饰符可实现全局匹配:
testRegexExec() {
// 案例1:提取URL中的域名
const url = "https://www.example.com/path?name=test";
const urlRegex = /https?:\/\/([\w.]+)/;
const urlResult = urlRegex.exec(url);
if (urlResult) {
console.log("完整匹配:", urlResult[0]); // https://www.example.com
console.log("提取域名:", urlResult[1]); // www.example.com
}
// 案例2:全局匹配提取所有数字
const str = "hello123 world456! 789";
const numRegex = /\d+/g; // g修饰符开启全局匹配
let numMatch;
while ((numMatch = numRegex.exec(str)) !== null) {
console.log(`数字:${numMatch[0]},位置:${numMatch.index}`);
}
// 输出:
// 数字:123,位置:5
// 数字:456,位置:14
// 数字:789,位置:20
}
四、JSON字段正则校验实战
整合JsonUtil(JSON读取)和ValidationUtil(静态校验工具),完成本地JSON文件的字段合法性校验。
1. 定义数据模型
model/UserInfo.ets
/**
* 用户信息接口:与user_info.json字段一一对应
*/
export interface UserInfo {
id: number;
name: string;
phone: string;
email: string;
password: string;
birthday: string;
}
model/RegexModel.ets
/**
* 正则校验规则的类型约束:统一规则结构
*/
export interface RegexValidatorType {
phone: RegExp; // 手机号正则
email: RegExp; // 邮箱正则
password: RegExp; // 密码正则
date: RegExp; // 基础日期正则
dateAdvanced: RegExp;// 进阶日期正则(限制月/日范围)
}
2. 定义正则常量
constant/RegexValidator.ets
import { RegexValidatorType } from "../model/RegexModel";
/**
* 正则校验规则常量:集中管理,便于维护
*/
export const RegexValidator: RegexValidatorType = {
phone: /^1[3-9]\d{9}$/,
email: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,
password: /^(?=.*\d)(?=.*[a-zA-Z])(?=.*[!@#$%^&*]).{8,20}$/,
date: /^\d{4}-\d{2}-\d{2}$/,
dateAdvanced: /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/
};
3. JSON读写工具
utils/JsonUtil.ets
import { Context } from '@kit.AbilityKit';
/**
* JSON工具类:读取rawfile目录下的JSON文件(适配API12)
*/
export class JsonUtil {
/**
* 异步读取rawfile中的JSON文件
* @param context 应用上下文(需通过getUIContext().getHostContext()获取)
* @param fileName JSON文件名(如user_info.json)
* @returns JSON字符串
*/
public static async readRawFileJson(context: Context, fileName: string): Promise<string> {
try {
const fileContent = await context.resourceManager.getRawFileContent(fileName);
return String.fromCharCode.apply(null, new Uint8Array(fileContent));
} catch (error) {
console.error(`读取JSON失败:${(error as Error).message}`);
throw new Error(`文件${fileName}不存在或格式错误`);
}
}
}
4. 静态校验工具类(核心)
utils/ValidationUtil.ets
import { UserInfo } from '../model/UserInfo';
import { RegexValidator } from '../constant/RegexValidator';
import { JsonUtil } from './JsonUtil';
import { Context } from '@kit.AbilityKit';
/**
* 静态校验工具类:无需实例化,直接通过类名调用
*/
export class ValidationUtil {
/**
* 校验用户信息字段合法性
* @param user 用户信息对象
* @returns 错误信息数组(空数组=校验通过)
*/
public static validateUser(user: UserInfo): string[] {
const errors: string[] = [];
// 空值前置校验(私有方法复用)
const checkEmpty = (value: string, field: string) => {
if (!value?.trim()) errors.push(`${field}不能为空`);
};
checkEmpty(user.phone, "手机号");
checkEmpty(user.email, "邮箱");
checkEmpty(user.password, "密码");
checkEmpty(user.birthday, "生日");
// 格式校验(仅当非空时执行)
if (user.phone?.trim() && !RegexValidator.phone.test(user.phone)) {
errors.push("手机号格式错误(11位有效数字,如13800138000)");
}
if (user.email?.trim() && !RegexValidator.email.test(user.email)) {
errors.push("邮箱格式错误(示例:user@example.com)");
}
if (user.password?.trim() && !RegexValidator.password.test(user.password)) {
errors.push("密码需8-20位,含数字+字母+特殊字符(!@#$%^&*)");
}
if (user.birthday?.trim() && !RegexValidator.dateAdvanced.test(user.birthday)) {
errors.push("生日格式错误(yyyy-mm-dd,如1999-01-01)");
}
return errors;
}
/**
* 一体化流程:读取JSON → 反序列化 → 正则校验
* @param context 应用上下文
*/
public static async processUserJson(context: Context) {
try {
// 1. 读取JSON文件
const jsonStr = await JsonUtil.readRawFileJson(context, "user_info.json");
console.log("读取的JSON:", jsonStr);
// 2. 反序列化为UserInfo对象
const user: UserInfo = JSON.parse(jsonStr);
// 3. 执行正则校验
const errors = ValidationUtil.validateUser(user);
// 4. 输出校验结果
if (errors.length === 0) {
console.log("✅ 用户信息全部合法!");
} else {
console.log("❌ 校验失败:");
errors.forEach((err, idx) => console.log(` ${idx+1}. ${err}`));
}
} catch (error) {
console.error("处理JSON失败:", error);
}
}
}
5. 测试用JSON文件
resources/rawfile/user_info.json
{
"id": 1001,
"name": "散修",
"phone": "13800138000",
"email": "sanxiu@example.com",
"password": "Test123!",
"birthday": "1999-01-01"
}
6. 页面调用与演示
pages/Index.ets
import { ValidationUtil } from '../utils/ValidationUtil';
import { Context } from '@kit.AbilityKit';
@Entry
@Component
struct Index {
// 获取API12兼容的应用上下文
private context: Context = this.getUIContext().getHostContext() as Context;
// 正则创建方式测试
testRegexCreate() {
// 方式1:字面量形式(推荐)- 语法简洁、性能高,适用于固定模式
const phoneRegex = /^1[3-9]\d{9}$/;
// 方式2:构造函数形式 - 适用于动态拼接模式,需注意转义符\\
const phonePrefix = '1[3-9]';
const phoneSuffix = '\\d{9}'; // 字符串中\需双重转义
const phoneRegex2 = new RegExp(`^${phonePrefix}${phoneSuffix}$`);
// 测试结果(两者等价)
console.log(`${phoneRegex.test("13800138000")}`); // true
console.log(`${phoneRegex2.test("13800138000")}`); // true
}
// 手机号正则测试
testRegexTel() {
const phoneRegex = /^1[3-9]\d{9}$/;
console.log(`${phoneRegex.test("13800138000")}`); // true
console.log(`${phoneRegex.test("12800138000")}`); // false(第二位非法)
console.log(`${phoneRegex.test("138001380")}`); // false(长度不足)
}
// 强密码正则测试
testStrongPassword() {
const pwdRegex = /^(?=.*\d)(?=.*[a-zA-Z])(?=.*[!@#$%^&*]).{8,20}$/;
console.log(`${pwdRegex.test("Test123!")}`); // true
console.log(`${pwdRegex.test("12345678")}`); // false(无字母/特殊字符)
}
// 日期正则测试
testRegexDate() {
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
console.log(`${dateRegex.test("2024-06-01")}`); // true
console.log(`${dateRegex.test("2024/06/01")}`); // false
console.log(`${dateRegex.test("2024-6-1")}`); // false
}
// test()方法测试
testRegexTest() {
const emailRegex = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
const emailList = ["user@example.com", "invalid-email", "user@.com"];
emailList.forEach((email, idx) => {
console.log(`邮箱${idx+1} [${email}]:${emailRegex.test(email)}`);
});
}
// exec()方法测试
testRegexExec() {
// 案例1:提取URL中的域名
const url = "https://www.example.com/path?name=test";
const urlRegex = /https?:\/\/([\w.]+)/;
const urlResult = urlRegex.exec(url);
if (urlResult) {
console.log("完整匹配:", urlResult[0]); // https://www.example.com
console.log("提取域名:", urlResult[1]); // www.example.com
}
// 案例2:全局匹配提取所有数字
const str = "hello123 world456! 789";
const numRegex = /\d+/g; // g修饰符开启全局匹配
let numMatch:RegExpExecArray|null;
while ((numMatch = numRegex.exec(str)) !== null) {
console.log(`数字:${numMatch[0]},位置:${numMatch.index}`);
}
}
// 测试正则基础语法
testRegexBasic() {
this.testRegexCreate();
this.testRegexTel();
this.testStrongPassword();
this.testRegexDate();
this.testRegexTest();
this.testRegexExec();
}
// 页面加载时执行基础测试
aboutToAppear(): void {
this.testRegexBasic();
}
build() {
Column({ space: 20 }) {
Text("正则+JSON校验实战")
.fontSize(24)
.fontWeight(FontWeight.Bold);
Button("执行用户信息校验")
.width('80%')
.height(50)
.backgroundColor('#007DFF')
.onClick(async () => {
// 调用静态工具类方法
await ValidationUtil.processUserJson(this.context);
});
Text("请打开Logcat查看输出结果")
.fontSize(14)
.fontColor(Color.Grey);
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
}
7. 运行结果说明
- 运行环境:必须使用真机/模拟器(预览器不支持
resourceManager接口); - 预期日志:
true true true false false true false false 邮箱1 [user@example.com]:true 邮箱2 [invalid-email]:false 邮箱3 [user@.com]:false 完整匹配: https://www.example.com 提取域名: www.example.com 数字:123,位置:5 数字:456,位置:14 数字:789,位置:20 读取的JSON: {"id":1001,"name":"散修","phone":"13800138000","email":"sanxiu@example.com","password":"Test123!","birthday":"1999-01-01"} ✅ 用户信息全部合法! - 异常测试:修改
user_info.json中手机号为123456,日志会输出:❌ 校验失败: 1. 手机号格式错误(11位有效数字,如13800138000)
五、常见错误与避坑指南
1. 转义字符错误(高频)
- 问题:构造函数创建正则时写
\d导致匹配失效; - 原因:字符串中
\是转义符,需双重转义; - 解决:字面量
/\d{4}/→ 构造函数new RegExp("\\d{4}")。
2. 贪婪匹配陷阱
- 问题:
/<.*>/匹配<div>内容</div>时会匹配整个字符串; - 原因:
*是贪婪量词,尽可能匹配更多字符; - 解决:使用非贪婪量词
*?,正则改为/<.*?>/。
3. 边界符遗漏
- 问题:
/1[3-9]\d{9}/会匹配13800138000abc为合法手机号; - 原因:未加
^/$,仅匹配子串而非整串; - 解决:严格匹配
/^1[3-9]\d{9}$/。
4. 预览器环境限制
- 问题:调用
JsonUtil提示resourceManager未定义; - 原因:预览器不支持设备相关API;
- 解决:始终用真机/模拟器运行文件读写相关代码。
5. 复杂正则性能问题
- 问题:嵌套量词(如
((a+)+)+)匹配长字符串时超时; - 解决:简化正则、使用非捕获分组
(?:)、分步校验。
六、实用工具推荐
- 在线正则测试:Regex101(https://regex101.com/)→ 语法高亮、实时预览、分组提取;
- 编辑器插件:VS Code「Regex Previewer」→ 实时预览匹配结果;
- 模板库:GitHub「Regex Patterns」→ 现成的手机号/身份证/邮箱正则模板。
七、内容总结
- 正则核心:由元字符(匹配规则)、量词(匹配次数)、修饰符(匹配行为)组成,
^/$是严格匹配的关键。 - ArkTS方法:
test()用于格式校验(返回布尔值),exec()用于内容提取(结合g实现全局匹配)。 - 工程化落地:将校验逻辑封装为
ValidationUtil静态工具类,结合JsonUtil完成“读取JSON→反序列化→正则校验”的完整链路。 - 避坑关键:构造函数正则需双重转义、避免贪婪匹配、添加边界符、适配鸿蒙真机运行环境。
八、代码仓库
- 工程名称:
RegexJsonDemo - 仓库地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git
九、下节预告
下一节将整合正则校验、JSON处理、MVVM架构等知识点,完善商品管理项目:
- 商品数据模型重构:正则校验商品编号、价格、生产日期等字段;
- 用户登录模块开发:正则校验账号密码,实现“登录验证→商品数据加载”的完整业务;
- 异常处理体系:全流程添加
try/catch,统一错误提示与日志输出。
浙公网安备 33010602011771号