JavaScript 正则表达式

JavaScript 正则表达式

Jack Lee 的 CSDN 博客
邮箱 :291148484@163.com
CSDN 主页https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343
本文地址
相关文章推荐:

目 录

1. 概述

2. 字符串规则描述符

3 创建正则表达式对象的方法

4. RegExp 对象

附录: 查询

参考资料


1. 概述

正则表达式用于描述各种复杂的字符串关系,使用正则表达式能够更加灵活便捷地处理字符串,它是使用单个字符串来描述、匹配一系列符合某个句法规则的字符串搜索模式。

2. 字符串规则描述符

>点击此处可以直接查询元字符表

2.1 定位符:描述字符的边界

符号描述说明
^匹配一个字符串的起始字符如果多行标志被设置为 true,那么也匹配换行符后紧跟的位置。
$匹配一个字符串的结尾字符如果多行标志被设置为 true,那么也匹配换行符前的位置。
\b匹配一个单词的边界-
\B匹配单词边界相当于\b匹配的反集

2.2 限定符:描述重复匹配的次数

符号描述说明
?匹配该限定符前的字符01等价于 {0,1},如 colou?r 可以匹配colourcolor
+匹配该限定符前的字符1等价于 {1,},如 hel+o可以匹配helohellohelllo、…
*匹配该限定符前的字符0等价于 {0,},如 hel*o可以匹配heohelohellohelllo、…
{n}匹配该限定符前的字符nhel{2}o只可以匹配hello
{n,}匹配该限定符前的字符最少n次hel{2,}o可以匹配hellohelllo、…
{n,m}匹配该限定符前的字符最少n次最多m次hel{2,3}o只可以匹配hellohelllo

【例】写出从字符串string的任意一行中匹配颜色名颜色值的正则表达式:

// 待提取的字符串
var str = `$navajowhite:          #FFDEAD !default;
$moccasin:             #FFE4B5 !default;
$bisque:               #FFE4C4 !default;
$mistyrose:            #FFE4E1 !default;
$blanchedalmond:       #FFEBCD !default;
$lavenderblush:        #FFF0F5 !default;`
目标表达式说明
提取颜色名'/\w{1,15}/考虑到上面颜色名单词最长没有超过15个字符串,最短肯定得有字符,所以写了{1,15},但也可以是其它的合理值即可。
提取颜色值'/#.{6}/',颜色值由#符号开头,一共由六位十六进制数字,十六进制数字为是个阿拉伯数字字符加A、B、C、D、E、F五个英文字符构成。

2.3 字符描述

符号描述说明
\d匹配任意数字
\s匹配任意空白符
\w匹配任意字母、数字、下划线、汉字等
\D匹配任意数字
\S匹配任意空白符
\W匹配除了字母、数字、下划线、汉字以外的字符
.匹配除了换行符以外的任意字符
形式描述说明
[A-Z]区间匹配,匹配字母表该区间所有大写字母[C-F]匹配字符C、D、E、F
[a-z]区间匹配,匹配字母表该区间所有小写字母[c-f]匹配字符c、d、e、f
[0-9]区间匹配,匹配该区间内的所有数字[3-6]匹配字符3、4、5、6
[ABCD]列表匹配,匹配[]中列出的所有字母如这里列出的A、B、C、D都会被匹配
[^ABCD]列表排除,匹配除了[]中列出的字符外的所有字符如这里列出的A、B、C、D都会被排除而匹配其它字符

例如,匹配IPV4地址:

var url = 'https://127.0.0.1:8888/Home/Welcome';
var re = /[1-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[1-9]{1,3}/;

var res = re[Symbol.matchAll](url);
console.log(Array.from(res, x => x[0])[0])

Out[1]:127.0.0.1

2.4 管道符:“或”匹配逻辑

管道符就是一根竖线:|,再正则表达式中标识”或“。由于匹配方向时从左向右进行的,假如有两个正则表达式AB,那么使用 A|B 匹配字符串时,只要前一个样式完全匹配成功,那么后一个就不再匹配。

2.5 转义字符:将特殊符号标识为普通字符

在正则表达式中用于标识特殊含义的符号如.用于标识一个任意的非换行符字符,^标识起始字符,等等。但是我们希望匹配到这些字符本身也是经常遇到的情况,如IPV4地址使用.作为分割符。因此我们如果需要完整提取IPV4地址就需要表示.本身。由于直接使用点已经有了其它的含义,因此我们使用一个\号进行转义,即使用\.来表示点(.)。其它在正则中有特殊含义的符号也可以使用类似的方式。

2.6 分组

与数学计算中采用小括号()进行算式分组一样,正则模板也能够分组表达,目的是将某一部分正则模板作为一个整体表达,例如模板:

var str = '4aderf weew16a dawedb_ewwecs aswed 6aew';
var re = /(we|ew){1}/g;
var res = re[Symbol.matchAll](str);

console.log(Array.from(res1, x => x[0]))

Out[]: [‘we’, ‘ew’, ‘we’, ‘ew’, ‘we’, ‘we’, ‘ew’]

这里将(we|ew)作为一个组,{n}对改组指定重复匹配的次数。

3 创建正则表达式对象的方法

以下三种方法创建都是可以的

  • /pattern/flags
  • new RegExp(pattern[, flags])
  • RegExp(pattern[, flags])

3.1 正则对象的字面量

字面量用于表达源代码中一个固定值。在 JavaScript 中字正则面量也可以用于创建正则表达式对象,当表达式被赋值时,字面量形式提供正则表达式的编译状态,当正则表达式保持为常量时使用字面量。例如当你在循环中使用字面量构造一个正则表达式时,正则表达式不会在每一次迭代中都被重新编译(recompiled)。

3.2 通过调用RegExp对象的构造函数创建

var re = /ab+c/;

3.3 直接写正则表达式字面量创建

var re = new RegExp("ab+c");

3.4 比较

项目特定场景说明
字面量当表达式被赋值时,字面量形式提供正则表达式的编译状态当正则表达式保持为常量时使用字面量例如当你在循环中使用字面量构造一个正则表达式时,正则表达式不会在每一次迭代中都被重新编译
构造函数正则表达式对象的构造函数,如 new RegExp('ab+c') 提供了正则表达式运行时编译如果你知道正则表达式模式将会改变,或者你事先不知道什么模式,而是从另一个来源获取,如用户输入,这些情况都可以使用构造函数-

4. RegExp 对象

RegExp 对象用于将文本与一个模式匹配。

4.1 构造函数 RegExp()

RegExp()构造函数用于创建一个RegExp对象,这与直接使用字面量创建的方式在多数情况下是可以通用的。
以下三个表达式创建相同的正则表达式:

/ab+c/i
new RegExp(/ab+c/, 'i') // 字面量
new RegExp('ab+c', 'i') // 构造函数

参数

pattern :正则模板

正则表达式的文本。

从ES5开始,这也可以是另一个RegExp对象或文字(仅用于两个RegExp构造函数符号)。模式可以包含特殊字符special characters来匹配比字面值字符串更广泛的值范围。

flags (标志)

如果指定, flags 是包含要添加的标志的字符串。ES6开始,如果为模式提供了一个对象,flags字符串将替换该对象的任何标志(并且lastIndex将重置为0)。如果没有指定flags并且提供了一个正则表达式对象,则该对象的flags(和lastIndex值)将被复制。flags 可包含下列任何字符的组合:

符号描述说明
g全局匹配找到所有的匹配,而不是在第一个匹配之后停止。
i忽略大小写如果u标志也被启用,使用Unicode大小写折叠。
m多行匹配将开始和结束字符(^ and $)视为在多行上工作。换句话说,匹配每一行的开头或结尾each line (由\n或者\r 分隔),而不仅仅是整个输入字符串的开头或结尾。
s点号匹配所有字符允许. 去匹配新的行
uunicodepattern 视为 Unicode 码位序列。 参考 二进制字符串
ysticky,粘性匹配仅从目标字符串中此正则表达式的 lastIndex 属性指示的索引中匹配。不尝试从任何后续索引中匹配

flag 不写在正则表达式里,标记位于表达式之外,格式为:

/pattern/flags

4.2 属性

4.2.1 静态属性

1. get RegExp[@@species]

species 是一个访问器属性,它返回 RegExp 对象 的默认构造器。子类构造器可能会覆盖它,来修改构造器的指派。

原始对象中的 species 属性

species属性返回默认的构造函数,即RegExp对象的RegExp构造函数:

RegExp[Symbol.species]; // RegExp()函数
派生对象中的 species 属性

在派生的集合对象中(例如,您的自定义regexp MyRegExp),MyRegExp种类是MyRegExp构造函数。但是,您可能希望覆盖它,以便在派生类方法中返回父RegExp对象:

class MyRegExp extends RegExp {
  // 将 MyRegExp species 覆盖到父 RegExp 的构造函数
  static get [Symbol.species]() { return RegExp; }
}

例如:

class MyRegExp extends RegExp {
  // 将MyRegExp种类覆盖到父RegExp构造函数
  static get [Symbol.species]() {
    return RegExp;
  }
}

const regex1 = new MyRegExp('foo', 'g');

console.log(regex1.test('football'));
// 期望输出: true
2 RegExp.lastIndex

仅当正则表达式实例使用 g 标志指示全局搜索使用 y 标志指示粘性搜索时,才设置此属性。#知识点链接

lastIndexRegExp 实例的读/写整数属性,指定开始下一个匹配的索引。lastIndex 不是 RegExp原型的属性,而是仅从 RegExp 实例中暴露出来。
对给定输入调用 test()exec() 时,适用以下规则:

  • 如果lastIndex 大于 输入的长度,exec()test() 将找不到匹配,lastIndex 将被设置为0
  • 如果lastIndex 小于等于 输入的长度,exec()test() 将尝试匹配从 lastIndex 开始的输入。
    • 如果 exec()test() 找到匹配项,lastIndex则将设置为输入中匹配字符串的结尾位置。
    • 如果 exec() 或未 test() 找到匹配项,lastIndex则将设置为 0。
例子

考虑以下语句序列:

var re = /(hi)?/g;

匹配空字符串:

console.log(re.exec('hi'));
console.log(re.lastIndex);

返回 lastIndex 等于2的["hi", "hi"]

console.log(re.exec('hi'));
console.log(re.lastIndex);

返回 ["", undefined] ,一个空数组,其第零个元素是匹配字符串。在本例中,空字符串是因为lastIndex是2(现在仍然是2),hi的长度是2。

4.2.2 实例属性

属性名描述说明
RegExp.prototype.flags含有 RegExp 对象 flags 的字符串
RegExp.prototype.dotAll. 是否要匹配新行(newlines
RegExp.prototype.global针对字符串中所有可能的匹配项测试正则表达式,还是仅针对第一个匹配项
RegExp.prototype.ignoreCase匹配文本的时候是否忽略大小写
RegExp.prototype.multiline是否进行多行搜索
RegExp.prototype.source返回一个值为当前正则表达式对象的模式文本的字符串该字符串不会包含正则字面量两边的斜杠以及任何的标志字符
RegExp.prototype.sticky仅从正则表达式的 lastIndex 属性表示的索引处搜索只读属性
RegExp.prototype.unicodeUnicode 功能是否开启只读属性

1. flags 属性

含有 RegExp 对象 flags 的字符串。

flags属性中的标志以字典序排序(从左到右,即"gimuy")。

属性描述符
RegExp.prototype.flags 属性的属性特性:
writablefalse
enumerablefalse
configurabletrue
例子
/foo/ig.flags;   // "gi"
/bar/myu.flags;  // "muy"

2. dotAll 属性

. 是否要匹配新行(newlines)。

dotAll 属性表明是否在正则表达式中一起使用"s"修饰符(引入/s修饰符,使得.可以匹配任意单个字符)。
dotAll 是一个只读的属性,属于单个正则表达式实例。
如果使用了"s"修饰符,dotAll 的值将返回Boolean类型的true,否则将返回false。“s"修饰符表示,特殊字符”."应另外匹配字符串中的下述行终结符(line terminator characters),否则将会失配:

  • U+000A 换行符(“\n”)
  • U+000D 回车符(“\r”)
  • U+2028 行分隔符(line separator)
  • U+2029 段分隔符(paragraph separator)
    这实际上意味着".“将会匹配任意的单个Unicode Basic Multilingual Plane (BMP)字符。若要使其与astral字符(大于\uFFFF的Unicode字符)匹配,你应当使用"u”(Unicode)修饰符。一起使用这两个修饰符,"."将无一例外地匹配任意Unicode字符。
属性描述符
RegExp.prototype.dotAll 属性的属性特性:
writablefalse
enumerablefalse
configurabletrue

3. global 属性

针对字符串中所有可能的匹配项测试正则表达式,还是仅针对第一个匹配项。

global 的值是布尔对象,如果使用了 “g” 标志,则返回 true;否则返回 false。 “g” 标志意味着正则表达式应该测试字符串中所有可能的匹配。

你无法直接更改此属性。

属性描述符
RegExp.prototype.global 属性的属性特性:
writablefalse
enumerablefalse
configurablefalse
例子
var regex = new RegExp("foo", "g")

console.log(regex.global) // true

4. ignoreCase 属性

匹配文本的时候是否忽略大小写。

ignoreCase 的值是布尔对象,如果使用了"i" 标志,则返回 true;否则,返回 false。“i” 标志意味着在字符串进行匹配时,应该忽略大小写。

你无法直接更改此属性。

属性描述符
RegExp.prototype.ignoreCase 属性的属性特性:
writablefalse
enumerablefalse
configurablefalse
例子
var regex = new RegExp("foo", "i")

console.log(regex.ignoreCase) // true

5. multiline 属性

是否进行多行搜索。

multiline 是一个布尔对象,如果使用了 “m” 标志,则返回 true;否则,返回 false。“m” 标志意味着一个多行输入字符串被看作多行。例如,使用 “m”,“^” 和 “$” 将会从只匹配正则字符串的开头或结尾,变为匹配字符串中任一行的开头或结尾。

你无法直接更改此属性。

属性描述符
RegExp.prototype.multiline 属性的属性特性:
writablefalse
enumerablefalse
configurablefalse
例子
var regex = new RegExp("foo", "m")

console.log(regex.multiline) // true

6. source 属性

正则表达式的文本。
该属性返回一个值为当前正则表达式对象的模式文本的字符串,该字符串不会包含正则字面量两边的斜杠以及任何的标志字符。

例子
var regex = /fooBar/ig;

console.log(regex.source); // "fooBar",不包含 /.../ 和 "ig"。

7. sticky 属性

该属性是正则表达式对象的只读属性,表示搜索是否是 sticky(粘性),即仅从正则表达式的 lastIndex 属性表示的索引处搜索 。

sticky 的值是 Boolean ,并在 y 标志使用时为真; 否则为假。y 标志指示,仅从正则表达式的 lastIndex 属性表示的索引处为目标字符串匹配(并且不会尝试从后续索引匹配)。如果一个表达式同时指定了 sticky 和 global,其将会忽略 global 标志。

你不能直接更改这个属性,它是只读的。

例子

使用带 sticky 标志的正则表达式:

var str = '#foo#';
var regex = /foo/y;

regex.lastIndex = 1;
regex.test(str); // true (译注:此例仅当 lastIndex = 1 时匹配成功,这就是 sticky 的作用)
regex.lastIndex = 5;
regex.test(str); // false (lastIndex 被 sticky 标志考虑到,从而导致匹配失败)
regex.lastIndex; // 0 (匹配失败后重置)

8.unicode 属性

Unicode 功能是否开启。即该属性表明正则表达式带有 "u" 标志。 unicode 是正则表达式独立实例的只读属性。

例如:

var regex = new RegExp('\u{61}', 'u');

console.log(regex.unicode); // true

4.3 实例方法

方法名描述说明
已废弃:RegExp.prototype.compile()用于在脚本执行过程中(重新)编译正则表达式已废弃,改用 RegExp 对象的构造函数
RegExp.prototype.exec()在该字符串中执行匹配项的搜索。
RegExp.prototype.test()该正则在字符串里是否有匹配。
RegExp.prototype[@@match]()对给定字符串执行匹配并返回匹配结果。
RegExp.prototype[@@matchAll]()对给定字符串执行匹配,返回所有匹配结果。
RegExp.prototype[@@replace]()给定新的子串,替换所有匹配结果。
RegExp.prototype[@@search]()在给定字符串中搜索匹配项,并返回在字符串中找到字符索引。
RegExp.prototype[@@split]()通过将给定字符串拆分为子字符串,并返回字符串形成的数组。
非标准:RegExp.prototype.toSource()方法返回一个字符串,代表当前对象的源代码该特性是非标准的,请尽量不要在生产环境中使用它!
RegExp.prototype.toString()返回表示指定对象的字符串。重写Object.prototype.toString()方法。

4.3.1 已废弃:RegExp.prototype.compile()方法

已废弃的compile() 方法被用于在脚本执行过程中(重新)编译正则表达式。与RegExp构造函数基本一样。
不推荐compile方法,可以使用 RegExp 构造函数来得到相同效果

语法
regexObj.compile(pattern, flags)
参数
  • pattern 正则表达式的文本

  • flags 如果指定,标志可以具有以下值的任意组合:

    标志(flag)描述
    g全局匹配
    i忽略大小写
    m多行;让开始和结束字符(^ 和 $)工作在多行模式工作(例如,^ 和 $ 可以匹配字符串中每一行的开始和结束(行是由 \n 或 \r 分割的),而不只是整个输入字符串的最开始和最末尾处。
    y黏度; 在目标字符串中,只从正则表达式的lastIndex属性指定的显示位置开始匹配(并且不试图从任何之后的索引匹配)。

使用compile方法:

regexObj.compile("new foo", "g");

相应改为使用构造函数的写法:

var regexObj = new RegExp("foo", "gi");

4.3.2 RegExp.prototype.exec()方法

在该字符串中执行匹配项的搜索。

// 匹配 "quick brown" 后跟 "jumps",忽略中间的字符
// 记住 "brown" 和 "jumps"
// 忽略大小写
var re = /quick\s(brown).+?(jumps)/ig;
var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog');
对象属性/索引描述例子
result[0]匹配的全部字符串Quick Brown Fox Jumps
[1], ...[*n* ]括号中的分组捕获[1] = Brown[2] = Jumps
index匹配到的字符位于原始字符串的基于0的索引值4
input原始字符串The Quick Brown Fox Jumps Over The Lazy Dog
relastIndex下一次匹配开始的位置25
ignoreCase是否使用了 “i” 标记使正则匹配忽略大小写true
global是否使用了 “g” 标记来进行全局的匹配.true
multiline是否使用了 “m” 标记使正则工作在多行模式(也就是,^ 和 $ 可以匹配字符串中每一行的开始和结束(行是由 \n 或 \r 分割的),而不只是整个输入字符串的最开始和最末尾处。)false
source正则匹配的字符串quick\s(brown).+?(jumps)
语法
regexObj.exec(str)
参数
  • str 要匹配正则表达式的字符串。
返回值
情形返回值
匹配成功返回一个数组(包含额外的属性 index 和 input ,参见下方表格),并更新正则表达式对象的 lastIndex 属性。完全匹配成功的文本将作为返回数组的第一项,从第二项起,后续每项都对应正则表达式内捕获括号里匹配成功的文本。
匹配失败返回 null,并将 lastIndex 重置为 0 。
例子:查找所有匹配

当正则表达式使用 "g" 标志时,可以多次执行 exec 方法来查找同一个字符串中的成功匹配。当你这样做时,查找将从正则表达式的 lastIndex 属性指定的位置开始。(test() 也会更新 lastIndex 属性)。注意,即使再次查找的字符串不是原查找字符串时,lastIndex 也不会被重置,它依旧会从记录的 lastIndex 开始。
例如:

var myRe = /ab*/g;
var str = 'abbcdefabh';
var myArray;
while ((myArray = myRe.exec(str)) !== null) {
  var msg = 'Found ' + myArray[0] + '. ';
  msg += 'Next match starts at ' + myRe.lastIndex;
  console.log(msg);
}

运行结果为:

Found abb. Next match starts at 3
Found ab. Next match starts at 9

4.3.3 RegExp.prototype.test()方法

test() 方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 true 或 false。

语法
regexObj.test(str)
参数
  • str 用来与正则表达式匹配的字符串
返回值
情形返回值
匹配成功true
匹配失败false
例子

测试hello是否在字符串str开始:

let str = 'hello world!';
let result = /^hello/.test(str);
console.log(result);

Out[]:true


4.3.4 RegExp.prototype[@@match]()方法

该方法为自定义 RegExp 子类中的匹配行为而存在。

该方法用于获取匹配结果。这个方法在 String.prototype.match() 的内部调用。例如,下面的两个方法返回相同结果。

'abc'.match(/a/);

/a/[Symbol.match]('abc');
语法
regexp[Symbol.match](str)
参数
参数描述
strmatch 的目标字符串
例子
直接调用

这个方法的使用方式和 String.prototype.match() 相同,不同之处是 this 和参数顺序。

var re = /[0-9]+/g;
var str = '2022-01-02';
var result = re[Symbol.match](str);
console.log(result);  // ["2022", "01", "02"]
在子类中调用

RegExp 的子类可以覆写 @@match方法来修改默认行为。

class MyRegExp extends RegExp {
  [Symbol.match](str) {
    var result = RegExp.prototype[Symbol.match].call(this, str);
    if (!result) return null;
    return {
      group(n) {
        return result[n];
      }
    };
  }
}

var re = new MyRegExp('([0-9]+)-([0-9]+)-([0-9]+)');
var str = '2016-01-02';
var result = str.match(re); // String.prototype.match 调用 re[@@match].
console.log(result.group(1)); // 2016
console.log(result.group(2)); // 01
console.log(result.group(3)); // 02

4.3.5 RegExp.prototype[@@matchAll]()方法

该方法返回对字符串使用正则表达式的所有匹配项。

语法
regexp[Symbol.matchAll](str)
参数
参数描述
str一个String的匹配对象。

例子
直接调用

除了this 的不同以及参数顺序的的差异,本方法的使用方法几乎与 StringmatchAll() 方法相同。

var re = /[0-9]+/g;
var str = '2016-01-02';
var result = re[Symbol.matchAll](str);

console.log(Array.from(result, x => x[0]));
// ["2016", "01", "02"]
在子类中使用@@matchAll

RegExp的子类可以重写@@matchAll方法来修改默认行为。例如,返回一个Array而不是iterator:

class MyRegExp extends RegExp {
  [Symbol.matchAll](str) {
    var result = RegExp.prototype[Symbol.matchAll].call(this, str);
    if (!result) {
      return null;
    } else {
      return Array.from(result);
    }
  }
}

var re = new MyRegExp('([0-9]+)-([0-9]+)-([0-9]+)', 'g');
var str = '2016-01-02|2019-03-07';
var result = str.matchAll(re);
console.log(result[0]); // [ "2016-01-02", "2016", "01", "02" ]
console.log(result[1]); // [ "2019-03-07", "2019", "03", "07" ]

4.3.6 RegExp.prototype[@@replace]()方法

该方法会在一个字符串中用给定的替换器,替换所有符合正则模式的匹配项,并返回替换后的新字符串结果。用来替换的参数可以是一个字符串或是一个针对每次匹配的回调函数。

语法
regexp[Symbol.replace](str, newSubStr|function)
参数
参数描述
str正则替换的目标字符串。
newSubStr (replacement)类型为 String 的替换器。支持大多数特殊的替换匹配模式; 见String.prototype.replace()页的Specifying a string as a parameter部分。
function (replacement)生成新的子字符串的回调函数替换器。
返回值

用替换器替换相应匹配项后的新字符串。


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jcLee95

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值