序号 | 系列文章 |
---|---|
7 | 【C#基础】C# 常用数据结构 |
8 | 【C#基础】C# 面向对象编程 |
9 | 【C# 基础】C# 异常处理操作 |
hello大家好啊,我是writer桑。前面一章已经学习了 C# 中的异常处理操作,那本章就开始学习 C# 程序中正则表达式的应用。关于正则表达式网上已经有很多现成的学习资源了,所以本章不会过多的描述正则表达式本身的内容,而是更多的介绍正则表达式在 C# 程序中的应用。文章的结尾也会给出一些笔者认为不错的资料和网站,方便自己学习的同时分享出来。感谢支持。
正则表达式,又称规则表达式(Regular Expression,通常缩写为 Regex),是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),是计算机科学的一个概念。正则表达式使用特定的语法以字符串的形式来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。
C# 等热门编程语言都支持正则表达式的实现。创建正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与运算符可以将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。 在 C# 中使用正则表达式需要用普通字符(例如字符 a 到 z)以及元字符组成的文字模式和引入 Regex 类结合使用。
正则表达式中的反斜杠字符 (\) 指示其后跟的字符是特殊字符(字符转义),或应按原义解释该字符。
下表列出转义字符:
转义字符 | 描述 | 模式 | 匹配 |
---|---|---|---|
\a | 与报警 (bell) 符 \u0007 匹配。 | \a | “Error!” + ‘\u0007’ 中的 “\u0007” |
\b | 在字符类中,与退格键 \u0008 匹配。 | [\b]{3,} | “\b\b\b\b” 中的 “\b\b\b\b” |
\t | 与制表符 \u0009 匹配。 | (\w+)\t | “item1\titem2\t” 中的 “item1\t” 和 “item2\t” |
\r | 与回车符 \u000D 匹配。 (\r 与换行符 \n不是等效的。) | \r\n(\w+) | “\r\nThese are\ntwo lines.” 中的 “\r\nThese” |
\v | 与垂直制表符 \u000B 匹配。 | [\v]{2,} | “\v\v\v” 中的 “\v\v\v” |
\f | 与换页符 \u000C 匹配。 | [\f]{2,} | “\f\f\f” 中的 “\f\f\f” |
\n | 与换行符 \u000A 匹配。 | \r\n(\w+) | “\r\nThese are\ntwo lines.” 中的 “\r\nThese” |
\e | 与转义符 \u001B 匹配。 | \e | “\x001B” 中的 “\x001B” |
\nnn | 使用八进制表示形式指定字符(nnn 由二位或三位数字组成)。 | \w\040\w | “a bc d” 中的 “a b” 和 “c d” |
\x nn | 使用十六进制表示形式指定字符(nn 恰好由两位数字组成)。 | \w\x20\w | “a bc d” 中的 “a b” 和 “c d” |
\c X \cx | 匹配 X 或 x指定的 ASCII 控制字符,其中 X 或 x 是控制字符的字母。 | \cC | “\x0003” 中的 “\x0003” (Ctrl-C) |
\u nnnn | 使用十六进制表示形式匹配 Unicode 字符(由 nnnn正确表示的四位数)。 | \w\u0020\w | “a bc d” 中的 “a b” 和 “c d” |
\ | 在后面带有不识别为本主题的此表和其他表中的转义符的字符时,与该字符匹配。 | \d+[±x*]\d+ | “(2+2) * 39" 中的 “2+2” 和 "39” |
示例代码:
// 要匹配的字符串内容
string content = "Error!" + '\u0007';
// 正则表达式
string RegexStr = @"\a";
// 使用Match()匹配
Match m = Regex.Match(content, RegexStr);
Console.WriteLine(m); // 输出 "\u0007"(报警声)
字符类与一组字符中的任何一个字符匹配。 字符类包括下表中列出的语言元素。
下表列出字符类:
字符类 | 描述 | 模式 | 匹配 |
---|---|---|---|
[character_group] | 匹配 character_group 中的任何单个字符。 默认情况下,匹配区分大小写。 | [mn] | “mat” 中的 “m”,“moon” 中的 “m” 和 “n” |
[^character_group] | 求反:与不在 character_group 中的任何单个字符匹配。 默认情况下, character_group 中的字符区分大小写。 | [^aei] | “reign” 中的 “r”、“g” 和 “n” |
[first-last] | 字符范围:与从第一个至最后一个的范围内的任何单个字符匹配。 | [A-Z] | “AB123” 中的 “A” 和 “B” |
. | 通配符:与除 \n 之外的任何单个字符匹配。若要匹配文本句点字符(. 或 \u002E),你必须在该字符前面加上转义符 (.)。 | a.e | “nave” 中的 “ave” “water” 中的 “ate” |
\p{name} | 与不在 name 指定的 Unicode 通用类别或命名块中的任何单个字符匹配。 | \P{Lu} \P{IsCyrillic} | “City” 中的 “i”、“t” 和 “y” “ДЖem” 中的 “e” 和 “m” |
\w | 与任何单词字符匹配。 | \w | “ID A1.3” 中的 “I”、“D”、“A”、“1” 和 “3” |
\W | 与任何非单词字符匹配。 | \W | “ID A1.3” 中的 " " 和 “.” |
\s | 与任何空白字符匹配。 | \w\s | “ID A1.3” 中的 "D " |
\S | 与任何非空白字符匹配。 | \s\S | “int __ctr” 中的 " _" |
\d | 与任何十进制数字匹配。 | \d | “4 = IV” 中的 “4” |
\D | 与任何不是十进制数的字符匹配。 | \D | “4 = IV” 中的 " “、”=“、” "、“I” 和 “V” |
示例代码:
// 要匹配的字符串内容
string content = "moon";
// 正则表达式
string RegexStr = @"[mn]";
// 使用Match()匹配
Match m = Regex.Match(content, RegexStr);
Console.WriteLine(m); // m
定位点或原子零宽度断言会使匹配成功或失败,具体取决于字符串中的当前位置,但它们不会使引擎在字符串中前进或使用字符。
下表列出定位点:
定位点 | 描述 | 模式 | 匹配 |
---|---|---|---|
^ | 默认情况下,必须从字符串的开头开始匹配;在多行模式中,必须从该行的开头开始。 | ^\d{3} | “901-333-” 中的 “901” |
$ | 默认情况下,匹配必须出现在字符串的末尾,或在字符串末尾的 \n 之前;在多行模式中,必须出现在该行的末尾之前,或在该行末尾的 \n 之前。 | -\d{3}$ | “-901-333” 中的 “-333” |
\A | 匹配必须出现在字符串的开头。 | \A\d{3} | “901-333-” 中的 “901” |
\Z | 匹配必须出现在字符串的末尾或出现在字符串末尾的 \n 之前。 | -\d{3}\Z | “-901-333” 中的 “-333” |
\z | 匹配必须出现在字符串的末尾。 | -\d{3}\z | “-901-333” 中的 “-333” |
\G | 匹配必须在上一个匹配结束的位置进行;如果以前没有匹配项,则从开始进行匹配的字符串中的位置开始。 | \G(\d) | “(1)(3)(5)[7](9)” 中的 “(1)”、“(3)” 和 “(5)” |
\b | 匹配必须出现在 \w (字母数字)和 \W (非字母数字)字符之间的边界上。 | \b\w+\s\w+\b | “them theme them them” 中的 “them theme” 和 “them them” |
\B | 匹配不得出现在 \b 边界上。 | \Bend\w*\b | “end sends endure lender” 中的 “ends” 和 “ender” |
示例代码:
// 要匹配的字符串内容
string content = "901-333-";
// 正则表达式
string RegexStr = @"^\d{3}";
// 使用Match()匹配
Match m = Regex.Match(content, RegexStr);
Console.WriteLine(m); // 901
分组构造描述了正则表达式的子表达式,通常用于捕获输入字符串的子字符串。
下表列出分组构造:
分组构造 | 描述 | 模式 | 匹配 |
---|---|---|---|
(subexpression) | 捕获匹配的子表达式并将其分配到一个从 1 开始的序号中。 | (\w)\1 | “deep” 中的 “ee” |
(? |
将匹配的子表达式捕获到一个命名组中。 | (? |
“deep” 中的 “ee” |
(? |
定义平衡组定义。 | (((?‘Open’\()[^\(\)]*)+((?‘Close-Open’\))[^\(\)]*)+)*(?(Open)(?!))$ | “3+2^((1-3)*(3-1))” 中的 “((1-3)*(3-1))” |
(?:subexpression) | 定义非捕获组。 | Write(?:Line)? | “Console.WriteLine()” 中的 “WriteLine” “Console.Write(value)” 中的 “Write” |
(?imnsx-imnsx: subexpression) | 应用或禁用子表达式中指定的选项。 | A\d{2}(?i:\w+)\b | “A12xl A12XL a12xl” 中的 “A12xl” 和 “A12XL” |
(?=subexpression) | 零宽度正预测先行断言。 | \w+(?=.) | “He is. The dog ran. The sun is out.” 中的 “is”、 “ran” 和 "out |
(?!subexpression) | 零宽度负预测先行断言。 | \b(?!un)\w+\b | “unsure sure unity used” 中的 “sure” 和 “used” |
(?<=subexpression) | 零宽度正回顾后发断言。 | (?<=19)\d{2}\b | “1851 1999 1950 1905 2003” 中的 “99”、"50"和 “05” |
(? | 零宽度负回顾后发断言。 | (? | “Hi woman Hi man” 中的 “man” |
(?>subexpression) | 非回溯(也称为"贪婪")子表达式。 | (?>a|ab)c | “ac” 中的 “ac”,"abc"中无匹配 |
示例代码:
// 要匹配的字符串内容
string content = "deep";
// \w 是匹配任何单词字符,\1 是匹配第一个()内匹配到的字符
// 如果是2,就是匹配第二个()匹配到的内容,以此类推。
string RegexStr = @"(\w)\1";
// 使用Match()匹配
Match m = Regex.Match(content, RegexStr);
Console.WriteLine(m); // ee
限定符指定在输入字符串中必须存在上一个元素(可以是字符、组或字符类)的多少个实例才能出现匹配项。
下表列出了限定符:
限定符 | 描述 | 模式 | 匹配 |
---|---|---|---|
* | 匹配上一个元素零次或多次。 | a.*c | “abcbc” 中的 “abcbc” |
+ | 匹配上一个元素一次或多次。 | “be+” | “been” 中的 “bee”、“bent” 中的 “be” |
? | 匹配上一个元素零次或一次。 | “rai?” | “rain” 中的 “rai” |
{n} | 匹配上一个元素恰好 n 次。 | “,\d{3}” | “1,043.6” 中的 “,043”、“9,876,543,210” 中的 “,876”、“,543” 和 “,210” |
{n,} | 匹配上一个元素至少 n 次。 | “\d{2,}” | “166”, “29”, “1930” |
{n,m} | 匹配上一个元素至少 n 次,但不多于 m 次。 | “\d{3,5}” | “166”, “17668” “193024” 中的 “19302” |
*? | 匹配上一个元素零次或多次,但次数尽可能少。 | a.*?c | “abcbc” 中的 “abc” |
+? | 匹配上一个元素一次或多次,但次数尽可能少。 | “be+?” | “been” 中的 “be”、“bent” 中的 “be” |
?? | 匹配上一个元素零次或一次,但次数尽可能少。 | “rai??” | “rain” 中的 “ra” |
{n}? | 匹配前面的元素恰好 n 次。 | “,\d{3}?” | “1,043.6” 中的 “,043”、“9,876,543,210” 中的 “,876”、“,543” 和 “,210” |
{n,}? | 匹配上一个元素至少 n 次,但次数尽可能少。 | “\d{2,}?” | “166”, “29” 和 “1930” |
{n,m}? | 匹配上一个元素的次数介于 n 和 m 之间,但次数尽可能少。 | “\d{3,5}?” | “166”, “17668” “193024” 中的 “193” 和 “024” |
示例代码:
// 要匹配的字符串内容
string content = "299.99";
// 匹配一个数字出现零次或更多次,再匹配符号. 与数字
string RegexStr = @"\d*\.\d";
// 使用Match()匹配
Match m = Regex.Match(content, RegexStr);
Console.WriteLine(m); // 299.9
反向引用允许在同一正则表达式中随后标识以前匹配的子表达式。 下表列出了 .NET 正则表达式支持的反向引用构造。
下表列出反向引用构造:
反向引用构造 | 描述 | 模式 | 匹配 |
---|---|---|---|
\number | 后向引用。 匹配编号子表达式的值。 | (\w)\1 | “seek” 中的 “ee” |
\k |
命名后向引用。 匹配命名表达式的值。 | (? |
“seek” 中的 “ee” |
代码示例:
// 要匹配的字符串内容
string content = "seek";
// 正则表达式
string RegexStr = @"(?\w)\k" ;
// 使用Match()匹配
Match m = Regex.Match(content, RegexStr);
Console.WriteLine(m); // ee
替换构造用于修改正则表达式以启用 either/or 匹配。 这些构造包括下表中列出的语言元素。
下表列出反向替换构造:
替换构造 | 描述 | 模式 | 匹配 |
---|---|---|---|
| | 匹配以竖线 (|) 字符分隔的任何一个元素。 | th(e|is|at) | “this is the day.” 中的 “the” 和 “this” |
(?( expression)yes|no) 或 (?( expression)yes) | 如果由 expression指定的正则表达式模式匹配,则匹配 yes ;否则,匹配可的 no 部分。expression 解释为零宽度的断言。 | (?(A)A\d{2}\b|\b\d{3}\b) | “A10 C103 910” 中的 “A10” 和 “910” |
(?( name)yes|no) 或 (?( name)yes) | 如果 name 或已命名或已编号的捕获组具有匹配,则匹配 yes;否则匹配可选的 no。 | (?< quoted>“)?(?(quoted).+?”|\S+\s) | “Dogs.jpg “Yiska playing.jpg”” 中的 "Dogs.jpg " 和 ““Yiska playing.jpg”” |
代码示例:
// 要匹配的字符串内容
string content = "that";
// 正则表达式
string RegexStr = @"th(e|is|at)";
// 使用Match()匹配
Match m = Regex.Match(content, RegexStr);
Console.WriteLine(m); // that
替换是替换模式(Regex 类的 Replace 方法)中支持的正则表达式语言元素。
下表列出了用于替换的字符:
字符 | 说明 | 模式 | 替换模式 | 输入字符串 | 结果字符串 |
---|---|---|---|---|---|
$number | 替换按组 number 匹配的子字符串。 | \b(\w+)(\s)(\w+)\b | $3$2$1 | “one two” | “two one” |
${name} | 替换按命名组 name 匹配的子字符串。 | \b(? |
${word2} ${word1} | “one two” | “two one” |
$$ | 替换字符"$"。 | \b(\d+)\s?USD | $$$1 | “103 USD” | “$103” |
$& | 替换整个匹配项的一个副本。 | ($*(\d*(.+\d+)?){1}) | **$& | “$1.30” | “**$1.30” |
$` | 替换匹配后的输入字符串的所有文本。 | B+ | $` | “AABBCC” | “AAAACC” |
$+ | 替换最后捕获的组。 | B+(C+) | $+ | “AABBCCDD” | AACCDD |
$_ | 替换整个输入字符串。 | B+ | $_ | “AABBCC” | “AAAABBCCCC” |
代码示例:
// 匹配模式
string pattern = @"\b(\w+)(\s)(\w+)\b";
// 替换模式
string replacement = "$3$2$1";
// 输入字符串
string input = "one two";
// Regex.Replace方法的应用
string result = Regex.Replace(input, pattern, replacement);
Console.WriteLine(result);
其他构造可修改某个正则表达式模式或提供有关该模式的信息。 下表列出了 .NET 支持的其他构造。
下表列出了其他构造:
构造 | 描述 | 实例 |
---|---|---|
(?imnsx-imnsx) | 在模式中间对诸如不区分大小写这样的选项进行设置或禁用。 | \bA(?i)b\w+\b 匹配 “ABA Able Act” 中的 “ABA” 和 “Able” |
(?# comment) | 内联注释。 该注释在第一个右括号处终止。 | \bA(?#Matches words starting with A)\w+\b |
# [至行尾] | X 模式注释。 该注释以非转义的 # 开头,并继续到行的结尾。 | (?x)\bA\w+\b#Matches words starting with A |
示例代码:
// 要匹配的字符串内容
string content = "ABA Able Act";
// 正则表达式
string RegexStr = @"\bA(?i)b\w+\b(?# This is my note)";
// 使用Match()匹配
Match m = Regex.Match(content, RegexStr);
Console.WriteLine(m); // ABA
列举一些在程序开发中常用的正则表达式操作案例,使用 C# 语言实现。
1, E-mail 地址
^ [a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(. [a-zA-Z0-9_-]+)+$
代码实现:
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void IsEmail(string email)
{
// 正则表达式
string RegexStr = @"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$";
// 使用Match()匹配
string m = Regex.Match(email, RegexStr).ToString();
if(String.IsNullOrEmpty(m))
{
Console.WriteLine("匹配为空");
}
else
{
Console.WriteLine(m);
}
}
public static void Main()
{
// 要匹配的字符串内容
string content = "[email protected]";
string content2 = "baidu.com";
IsEmail(content);
IsEmail(content2);
}
}
2,Internet URL
(https?|ftp|file): //[-A-Za-z0-9+&@#/%?=~_|!:,.;]+ [-A-Za-z0-9+&@#/%=~_|]
代码实现:
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void IsUrl(string url)
{
// 正则表达式
string RegexStr = @"(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
// 使用Match()匹配
string m = Regex.Match(url, RegexStr).ToString();
if(String.IsNullOrEmpty(m))
{
Console.WriteLine("匹配为空");
}
else
{
Console.WriteLine(m);
}
}
public static void Main()
{
// 要匹配的字符串内容
string content = "[email protected]";
string content2 = "http://www.baidu.com";
IsUrl(content);
IsUrl(content2);
}
}
3, 匹配首尾空白字符的正则表达式
^\s | \s$
代码实现:
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void HasBlank(string url)
{
// 正则表达式
string RegexStr = @"^\s|\s$";
// 使用Match()匹配
string m = Regex.Match(url, RegexStr).ToString();
if(String.IsNullOrEmpty(m))
{
Console.WriteLine("前后没有空白字符");
}
else
{
Console.WriteLine("前后有空白字符");
}
}
public static void Main()
{
// 要匹配的字符串内容
string content = " text ";
string content2 = "www.baidu.com";
HasBlank(content);
HasBlank(content2);
}
}
4,手机号码
^1 ([0-9]|4 |66|7 |9) [0-9] {8}$
代码实现:
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void IsPhone(string url)
{
// 正则表达式
string RegexStr = @"^1([0-9]|4|66|7|9)[0-9]{8}$";
// 使用Match()匹配
string m = Regex.Match(url, RegexStr).ToString();
if(String.IsNullOrEmpty(m))
{
Console.WriteLine("匹配为空");
}
else
{
Console.WriteLine(m);
}
}
public static void Main()
{
// 要匹配的字符串内容
string content = "110";
string content2 = "1388888888";
IsPhone(content);
IsPhone(content2);
}
}
5,电话号码(“XXX-XXXXXXX”,“XXX-XXXXXXXX”,“XXXX-XXXXXXX”,“XXXX-XXXXXXXX”,“XXXXXXX” 和 “XXXXXXXX”, 如0511-1234567、如021-12345678)
^(\d{3,4}-)?\d{7,8}$
代码实现:
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void IsTel(string url)
{
// 正则表达式
string RegexStr = @"^(\d{3,4}-)?\d{7,8}$";
// 使用Match()匹配
string m = Regex.Match(url, RegexStr).ToString();
if(String.IsNullOrEmpty(m))
{
Console.WriteLine("匹配为空");
}
else
{
Console.WriteLine(m);
}
}
public static void Main()
{
// 要匹配的字符串内容
string content = "021-88888888";
string content2 = "1388888888";
IsTel(content);
IsTel(content2);
}
}
6,日期格式(2018-01-01只做粗略匹配,格式不限制,二月有30天等)
^\d{4}-\d{1,2}-\d{1,2}$
代码实现:
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void IsDate(string url)
{
// 正则表达式
string RegexStr = @"^\d{4}-\d{1,2}-\d{1,2}$";
// 使用Match()匹配
string m = Regex.Match(url, RegexStr).ToString();
if(String.IsNullOrEmpty(m))
{
Console.WriteLine("匹配为空");
}
else
{
Console.WriteLine(m);
}
}
public static void Main()
{
// 要匹配的字符串内容
string content = "1990-01-01";
string content2 = "2018-0909";
IsDate(content);
IsDate(content2);
}
}
在 C# 程序中,使用正则表达式则需要引入命名空间 System.Text.RegularExpressions 中的 Regex 方法。 Regex 类定义为表示不可变的正则表达式。
下表列出了 Regex 类中的一些常用的方法:
方法 | 描述 |
---|---|
IsMatch(String) | 指示 Regex 构造函数中指定的正则表达式在指定的输入字符串中是否找到了匹配项。 |
IsMatch(String, Int32) | 指示 Regex 构造函数中指定的正则表达式在指定的输入字符串中,从该字符串中的指定起始位置开始是否找到了匹配项。 |
IsMatch(String, String) | 指示所指定的正则表达式在指定的输入字符串中是否找到了匹配项。 |
Matches(String) | 在指定的输入字符串中搜索正则表达式的所有匹配项。 |
Matches(String, Int32) | 从字符串中的指定起始位置开始,在指定的输入字符串中搜索正则表达式的所有匹配项。 |
Matches(String, String) | 在指定的输入字符串中搜索指定的正则表达式的所有匹配项。 |
Replace(String, MatchEvaluator) | 在指定的输入字符串中,使用由 MatchEvaluator 委托返回的字符串替换与指定的正则表达式匹配的所有字符串。 |
Replace(String, MatchEvaluator, Int32) | 在指定的输入字符串内,使用 MatchEvaluator 委托返回的字符串替换与某个正则表达式模式匹配的字符串(其数目为指定的最大数目)。 |
Split(String) | 在由 Regex 构造函数指定的正则表达式模式所定义的位置,将输入字符串拆分为子字符串数组。 |
点击了解 Regex 类更多的用法。
推荐一些笔者认为不错的正则表达式学习资源,包括教程、测试工具、可视化网站、视频和书籍。
菜鸟教程提供了基础的编程技术教程,是国内许多编程爱好者必经的学习网站。 如果你是一个零基础的编程小白,那么菜鸟教程的正则表达式教程绝对是你入门的不错选择,菜鸟教程对初学者来说十分友好,可以帮助于初学者快速学习正则表达式的内容。
菜鸟工具是为开发设计人员提供的在线工具网。其中不仅提供了正则表达式的在线测试工具,还提供了像 python、c++ 等语言的在线调式编译工具, 菜鸟工具具备了大部分在编程学习过程所需要的学习资源,满足了开发、运维和设计等需求。
Regulex 是一款免费且支持可视化的正则表达式工具 ,可以帮助程序员理解什么是正则、怎么写正则表达式,有了这款工具就可以快速学会书写正则,看懂正则不再是难事。
《精通正则表达式》从正则表达式的基本概念、基本语法入手,着重介绍了正则表达式在数字验证、字符串验证、数字和字符串混合验证、HTML处理等各个方面的应用,并基于流行的程序语言或应用环境(如C#、ASP.NET、JSP、JavaScript、PHP),全面介绍了创建正则表达式的方法,以及正则表达式在Web环境中的各种应用。
这是在b站上的一位up主,名为奇乐编程学院录制的一个正则表达式的学习视频, 其中up主也推荐正则表达式相关的学习资源。 笔者觉得讲的不错,推荐出来让大家学习。
以上就是 C# 正则表达式的介绍啦,希望能够对大家有所帮助。望大家多多支持,你们的支持就是笔者创作最大的动力!