深入理解 JavaScript 中的 RegExp V flag (Unicode Property Escapes in regular expressions) 如何增强正则表达式的 Unicode 处理能力。

大家好,我是老码农,今天咱们来聊聊 JavaScript 正则表达式中的一个“V”字仇杀队的“V”—— v flag,也叫 Unicode Property Escapes。这玩意儿听起来高大上,但其实是个帮你更好地处理 Unicode 字符的小助手。

开场白:Unicode 的那些事儿

在说v flag 之前,咱们得先简单回顾一下 Unicode。早些年,ASCII 那128个字符还能勉强应付一下英语,但随着互联网全球化,各种语言都冒出来了,ASCII 就歇菜了。Unicode 就是来拯救世界的,它给每个字符都分配了一个唯一的数字,也就是码点(code point)。

但是,Unicode 字符集实在太大了,什么奇奇怪怪的符号都有。光靠 wds 这些简化的字符类,你根本没法精确匹配。比如,你想匹配所有的中文字符,用 w 肯定不行,因为它还会包含英文、数字和下划线。

进入正题:v flag 的闪亮登场

这个时候,v flag 就派上用场了。它允许你在正则表达式中使用 Unicode Property Escapes,也就是用 p{}P{} 这样的语法来匹配具有特定 Unicode 属性的字符。

  • p{...}:匹配具有指定属性的字符。
  • P{...}:匹配不具有指定属性的字符。

这俩货就像是正则表达式里的魔法咒语,能让你轻松驾驭 Unicode 字符。

v flag 的语法和用法

要使用 v flag,你需要在正则表达式的末尾加上 v。例如:

const regex = /p{Script=Han}+/v; // 匹配一个或多个汉字
const str = "你好世界!";
console.log(regex.test(str)); // 输出: true

注意,v flag 必须和 u flag 共存。这是因为 Unicode Property Escapes 本身就是 Unicode 特性,没有 u flag,JavaScript 就不会把正则表达式中的字符当成 Unicode 码点来处理。

Unicode Property Escapes 的种类

Unicode Property Escapes 分为两大类:

  1. General Category Properties(通用类别属性):描述字符的一般类型,比如字母、数字、标点符号等等。
  2. Script Properties(脚本属性):描述字符所属的文字系统,比如汉字、希腊字母、西里尔字母等等。

1. General Category Properties

General Category Properties 用两个字母的代码表示,例如:

  • Lu:大写字母(Uppercase letter)
  • Ll:小写字母(Lowercase letter)
  • Lt:词首字母大写的字母(Titlecase letter)
  • Lm:修饰字母(Modifier letter)
  • Lo:其他字母(Other letter)
  • Mn:非间距标记(Nonspacing mark)
  • Mc:间距组合标记(Spacing combining mark)
  • Me:封闭标记(Enclosing mark)
  • Nd:十进制数字(Decimal number)
  • Nl:字母数字(Letter number)
  • No:其他数字(Other number)
  • Ps:左括号(Punctuation, open)
  • Pe:右括号(Punctuation, close)
  • Pi:起始引号(Punctuation, initial quote)
  • Pf:结束引号(Punctuation, final quote)
  • Pc:连接标点符号(Punctuation, connector)
  • Pd:破折号(Punctuation, dash)
  • Po:其他标点符号(Punctuation, other)
  • Sm:数学符号(Symbol, math)
  • Sc:货币符号(Symbol, currency)
  • Sk:修饰符号(Symbol, modifier)
  • So:其他符号(Symbol, other)
  • Zs:空格分隔符(Separator, space)
  • Zl:行分隔符(Separator, line)
  • Zp:段落分隔符(Separator, paragraph)
  • Cc:控制字符(Other, control)
  • Cf:格式字符(Other, format)
  • Cs:代理对(Other, surrogate)
  • Co:私用字符(Other, private use)
  • Cn:未分配字符(Other, not assigned)

你可以用 p{General_Category=...} 或者简写形式 p{...} 来使用这些属性。

例如,匹配一个大写字母:

const regex = /p{Lu}/vu; // 注意 vu 的顺序无所谓
console.log(regex.test("A")); // 输出: true
console.log(regex.test("a")); // 输出: false

匹配一个数字:

const regex = /p{Nd}/vu;
console.log(regex.test("1")); // 输出: true
console.log(regex.test("a")); // 输出: false

2. Script Properties

Script Properties 用脚本名称表示,例如:

  • Han:汉字
  • Latin:拉丁字母
  • Greek:希腊字母
  • Cyrillic:西里尔字母
  • Arabic:阿拉伯字母
  • Hebrew:希伯来字母
  • Devanagari:梵文

你可以用 p{Script=...} 来使用这些属性。

例如,匹配一个汉字:

const regex = /p{Script=Han}/vu;
console.log(regex.test("你")); // 输出: true
console.log(regex.test("a")); // 输出: false

匹配一个希腊字母:

const regex = /p{Script=Greek}/vu;
console.log(regex.test("α")); // 输出: true
console.log(regex.test("a")); // 输出: false

实战演练:用 v flag 解决实际问题

光说不练假把式,咱们来几个实际的例子。

例子 1:提取字符串中的所有汉字

function extractChinese(str) {
  const regex = /p{Script=Han}/gvu;
  const result = str.match(regex);
  return result ? result.join('') : '';
}

const str = "Hello 你好 世界!123";
console.log(extractChinese(str)); // 输出: 你好世界

例子 2:验证用户输入的用户名是否只包含字母、数字和下划线,并且允许中文

function isValidUsername(username) {
  const regex = /^[wp{Script=Han}]+$/vu;
  return regex.test(username);
}

console.log(isValidUsername("老码农123")); // 输出: true
console.log(isValidUsername("old_coder")); // 输出: true
console.log(isValidUsername("old-coder")); // 输出: false

例子 3:计算字符串中汉字的个数

function countChineseCharacters(str) {
    const regex = /p{Script=Han}/gvu;
    const matches = str.matchAll(regex);
    let count = 0;
    for (const match of matches) {
        count++;
    }
    return count;
}

const str = "你好世界,Hello World!";
console.log(countChineseCharacters(str)); // 输出 4

v flag 的进阶用法

除了直接使用 General Category Properties 和 Script Properties,v flag 还支持一些更高级的用法。

  1. Boolean Math (布尔运算)

你可以使用 &(与)、|(或)、~(非)来组合不同的属性。

例如,匹配既是字母又是大写的字符:

const regex = /p{Lu & Letter}/vu; // Letter是Lu, Ll, Lt, Lm, Lo的简称
console.log(regex.test("A")); // 输出: true
console.log(regex.test("a")); // 输出: false
console.log(regex.test("1")); // 输出: false

匹配不是数字的字符:

const regex = /p{~Nd}/vu;
console.log(regex.test("a")); // 输出: true
console.log(regex.test("1")); // 输出: false
  1. Binary Properties (二元属性)

有些属性只有两种状态:true 或 false。例如:

  • Uppercase:是否为大写字母
  • Lowercase:是否为小写字母
  • Alphabetic:是否为字母
  • Ideographic:是否为表意文字(例如汉字)
  • Emoji:是否为表情符号

你可以直接使用这些属性,例如:

const regex = /p{Uppercase}/vu;
console.log(regex.test("A")); // 输出: true
console.log(regex.test("a")); // 输出: false

匹配一个表情符号:

const regex = /p{Emoji}/vu;
console.log(regex.test("😀")); // 输出: true
console.log(regex.test("a")); // 输出: false
  1. Unicode Block (Unicode 块)

Unicode 把字符集划分成一个个的块(block),你可以用 p{Block=...} 来匹配特定块中的字符。例如:

  • Basic_Latin:基本拉丁字母
  • CJK_Unified_Ideographs:CJK 统一表意符号(汉字)
  • Greek_and_Coptic:希腊字母和科普特字母

例如,匹配一个基本拉丁字母:

const regex = /p{Block=Basic_Latin}/vu;
console.log(regex.test("a")); // 输出: true
console.log(regex.test("你")); // 输出: false

一些需要注意的点

  • 性能问题:虽然 v flag 很强大,但它可能会降低正则表达式的性能,尤其是在处理大量文本时。所以,在性能敏感的场景下,要谨慎使用。
  • 浏览器兼容性v flag 的兼容性还可以,主流浏览器都支持。但是,为了兼容老版本的浏览器,你可能需要使用 Babel 等工具进行转译。
  • 属性名称大小写:Unicode Property Escapes 的属性名称是区分大小写的。例如,p{Script=Han}p{script=han} 是不同的。但是为了健壮性, 推荐按照规范来写.
  • 短横线和下划线:在属性名称中,短横线 - 和下划线 _ 是等价的。例如,p{General_Category=Lowercase_Letter}p{General-Category=Lowercase-Letter} 是一样的。

总结:v flag 的价值

v flag 就像是正则表达式的瑞士军刀,让你在处理 Unicode 字符时更加得心应手。它可以帮助你:

  • 更精确地匹配特定类型的字符。
  • 轻松处理各种语言的文本。
  • 编写更健壮、更易于维护的正则表达式。

表格总结:v flag 常用属性

属性类型 属性名称 描述 示例
General Category Lu 大写字母 /p{Lu}/vu.test("A") // true
General Category Ll 小写字母 /p{Ll}/vu.test("a") // true
General Category Nd 十进制数字 /p{Nd}/vu.test("1") // true
Script Han 汉字 /p{Script=Han}/vu.test("你") // true
Script Latin 拉丁字母 /p{Script=Latin}/vu.test("a") // true
Binary Uppercase 是否为大写字母 /p{Uppercase}/vu.test("A") // true
Binary Lowercase 是否为小写字母 /p{Lowercase}/vu.test("a") // true
Binary Emoji 是否为表情符号 /p{Emoji}/vu.test("😀") // true
Block Basic_Latin 基本拉丁字母块 /p{Block=Basic_Latin}/vu.test("a") // true
Boolean Math p{Lu & Alphabetic} 既是大写字母又是字母 /p{Lu & Alphabetic}/vu.test("A") // true
Negation p{~Nd} 不是数字的字符 /p{~Nd}/vu.test("a") // true

结束语:拥抱 Unicode,拥抱 v flag

Unicode 已经成为互联网世界的基石,掌握 Unicode Property Escapes 是每个 JavaScript 开发者必备的技能。v flag 就像一把钥匙,打开了 Unicode 的宝藏,让你在处理文本时更加游刃有余。希望今天的讲座能帮助你更好地理解和使用 v flag,在你的编程之路上更上一层楼!

好了,今天的分享就到这里,感谢大家的观看,我们下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注