深度优先

这个家伙好懒,除了文章什么都没留下

0%

一、概述

正则表达式用于文本内容的查找和替换。

正则表达式内置于其它语言或者软件产品中,它本身不是一种语言或者软件。

正则表达式在线工具


二、匹配单个字符

. 可以用来匹配任何的单个字符,但是在绝大多数实现里面,不能匹配换行符;

. 是元字符,表示它有特殊的含义,而不是字符本身的含义。如果需要匹配 . ,那么要用 进行转义,即在 . 前面加上 。

正则表达式一般是区分大小写的,但是也有些实现是不区分。

正则表达式

1
nam.

匹配结果

My name is Zheng.


三、匹配一组字符

[ ] 定义一个字符集合;

0-9、a-z 定义了一个字符区间,区间使用 ASCII 码来确定,字符区间在 [ ] 中使用。

- 只有在 [ ] 之间才是元字符,在 [ ] 之外就是一个普通字符;

^ 在 [ ] 中是取非操作。

应用

匹配以 abc 为开头,并且最后一个字母不为数字的字符串:

正则表达式

1
abc[^0-9]

匹配结果

  1. abcd
  2. abc1
  3. abc2

四、使用元字符

匹配空白字符

元字符 说明
[b] 回退(删除)一个字符
f 换页符
n 换行符
r 回车符
t 制表符
v 垂直制表符

rn 是 Windows 中的文本行结束标签,在 Unix/Linux 则是 n。

rnrn 可以匹配 Windows 下的空白行,因为它将匹配两个连续的行尾标签,而这正是两条记录之间的空白行;

匹配特定的字符类别

1. 数字元字符

元字符 说明
d 数字字符,等价于 [0-9]
D 非数字字符,等价于 1

2. 字母数字元字符

元字符 说明
w 大小写字母,下划线和数字,等价于 [a-zA-Z0-9_]
W 对 w 取非

3. 空白字符元字符

元字符 说明
s 任何一个空白字符,等价于 [fnrtv]
S 对 s 取非

x 匹配十六进制字符,0 匹配八进制,例如 x0A 对应 ASCII 字符 10,等价于 n。


五、重复匹配

  • + 匹配 1 个或者多个字符
  • \** * 匹配 0 个或者多个
  • ? 匹配 0 个或者 1 个

应用

匹配邮箱地址。

正则表达式

1
[\w.]+@\w+\.\w+

[w.] 匹配的是字母数字或者 . ,在其后面加上 + ,表示匹配多次。在字符集合 [ ] 里,. 不是元字符;

匹配结果

abc.def@qq.com

  • {n} 匹配 n 个字符
  • {m, n} 匹配 m~n 个字符
  • {m,} 至少匹配 m 个字符

* 和 + 都是贪婪型元字符,会匹配最多的内容。在后面加 ? 可以转换为懒惰型元字符,例如 *?、+? 和 {m, n}? 。

正则表达式

1
a.+c

由于 + 是贪婪型的,因此 .+ 会匹配更可能多的内容,所以会把整个 abcabcabc 文本都匹配,而不是只匹配前面的 abc 文本。用懒惰型可以实现匹配前面的。

匹配结果

abcabcabc


六、位置匹配

单词边界

b 可以匹配一个单词的边界,边界是指位于 w 和 W 之间的位置;B 匹配一个不是单词边界的位置。

b 只匹配位置,不匹配字符,因此 babcb 匹配出来的结果为 3 个字符。

字符串边界

^ 匹配整个字符串的开头,$ 匹配结尾。

^ 元字符在字符集合中用作求非,在字符集合外用作匹配字符串的开头。

分行匹配模式(multiline)下,换行被当做字符串的边界。

应用

匹配代码中以 // 开始的注释行

正则表达式

1
^\s*\/\/.*$

匹配结果

public void fun() {
// 注释 1
int a = 1;
int b = 2;
// 注释 2
int c = a + b;
}


七、使用子表达式

使用 ( ) 定义一个子表达式。子表达式的内容可以当成一个独立元素,即可以将它看成一个字符,并且使用 * 等元字符。

子表达式可以嵌套,但是嵌套层次过深会变得很难理解。

正则表达式

1
(ab){2,}

匹配结果

ababab

| 是或元字符,它把左边和右边所有的部分都看成单独的两个部分,两个部分只要有一个匹配就行。

正则表达式

1
(19|20)\d{2}

匹配结果

  1. 1900
  2. 2010
  3. 1020

应用

匹配 IP 地址。

IP 地址中每部分都是 0-255 的数字,用正则表达式匹配时以下情况是合法的:

  • 一位数字
  • 不以 0 开头的两位数字
  • 1 开头的三位数
  • 2 开头,第 2 位是 0-4 的三位数
  • 25 开头,第 3 位是 0-5 的三位数

正则表达式

1
((25[0-5]|(2[0-4]\d)|(1\d{2})|([1-9]\d)|(\d))\.){3}(25[0-5]|(2[0-4]\d)|(1\d{2})|([1-9]\d)|(\d))

匹配结果

  1. 192.168.0.1
  2. 00.00.00.00
  3. 555.555.555.555

八、回溯引用

回溯引用使用 n 来引用某个子表达式,其中 n 代表的是子表达式的序号,从 1 开始。它和子表达式匹配的内容一致,比如子表达式匹配到 abc,那么回溯引用部分也需要匹配 abc 。

应用

匹配 HTML 中合法的标题元素。

正则表达式

1 将回溯引用子表达式 (h[1-6]) 匹配的内容,也就是说必须和子表达式匹配的内容一致。

1
<(h[1-6])>\w*?<\/\1>

匹配结果

<h1>x</h1>
<h2>x</h2>
<h3>x</h1>

替换

需要用到两个正则表达式。

应用

修改电话号码格式。

文本

313-555-1234

查找正则表达式

1
(\d{3})(-)(\d{3})(-)(\d{4})

替换正则表达式

在第一个子表达式查找的结果加上 () ,然后加一个空格,在第三个和第五个字表达式查找的结果中间加上 - 进行分隔。

1
($1) $3-$5

结果

(313) 555-1234

大小写转换

元字符 说明
l 把下个字符转换为小写
u 把下个字符转换为大写
L 把L 和E 之间的字符全部转换为小写
U 把U 和E 之间的字符全部转换为大写
E 结束L 或者U

应用

把文本的第二个和第三个字符转换为大写。

文本

abcd

查找

1
(\w)(\w{2})(\w)

替换

1
$1\U$2\E$3

结果

aBCd


九、前后查找

前后查找规定了匹配的内容首尾应该匹配的内容,但是又不包含首尾匹配的内容。向前查找用 ?= 来定义,它规定了尾部匹配的内容,这个匹配的内容在 ?= 之后定义。所谓向前查找,就是规定了一个匹配的内容,然后以这个内容为尾部向前面查找需要匹配的内容。向后匹配用 ?<= 定义(注: javaScript 不支持向后匹配, java 对其支持也不完善)。

应用

查找出邮件地址 @ 字符前面的部分。

正则表达式

1
\w+(?=@)

结果

abc @qq.com

对向前和向后查找取非,只要把 = 替换成 ! 即可,比如 (?=) 替换成 (?!) 。取非操作使得匹配那些首尾不符合要求的内容。


十、嵌入条件

回溯引用条件

条件判断为某个子表达式是否匹配,如果匹配则需要继续匹配条件表达式后面的内容。

正则表达式

子表达式 (\() 匹配一个左括号,其后的 ? 表示匹配 0 个或者 1 个。 ?(1) 为条件,当子表达式 1 匹配时条件成立,需要执行 ) 匹配,也就是匹配右括号。

1
(\()?abc(?(1)\))

结果

  1. (abc)
  2. abc
  3. (abc

前后查找条件

条件为定义的首尾是否匹配,如果匹配,则继续执行后面的匹配。注意,首尾不包含在匹配的内容中。

正则表达式

?(?=-) 为前向查找条件,只有在以 - 为前向查找的结尾能匹配 d{5} ,才继续匹配 -d{4} 。

1
\d{5}(?(?=-)-\d{4})

结果

  1. 11111
  2. 22222-
  3. 33333-4444

十一、提取信息

文本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 查询一个套表pk

## 1.接口说明
查看给定名称的一个套表的pk
## 2.使用场景
用于套表后续操作例如,复制,停用,启用,发布等功能
## 3.接口调用说明

### 3.1请求类型
POST

### 3.2请求示例
> http://IP:port/nccloud/api/epm/form/queryformpk

### 3.3请求URL参数说明
| 参数 | 类型 | 是否必填 | 描述 |
|------|------|------|------|
| objname | string | 是 | 套表名称 |



> 请求参数JSON示例
\`\`\`
{
"objname":"YSB01"
}
\`\`\`

### 3.4返回参数说明
#### 3.4.1返回值
|参数说明|类型|描述|
|-------|---|----|
|pk_workbook|string|套表的pk|
#### 3.4.2正确返回示例
>返回值JSON示例:
\`\`\`
result: {
{
"success": true,
"data": {
"pk_workbook": "0001AG10000000004A29"
}
}
}
\`\`\`

提取内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public bool ContenTextRact(string str, out string msg, out string response, out string request)
{
response = string.Empty;
request = string.Empty;
msg = string.Empty;

Regex regex = new Regex("(>\\s请求参数JSON示例\n```(?<response>(.|\r|\n)*?)```)");
Match match = regex.Match(str);
if (match.Success)
{
response = match.Groups["response"].Value;
}
else
{
msg = "获取请求参数失败";
return false;
}
regex = new Regex("(>返回值JSON示例\\s\n```(?<request>(.|\r|\n)*?)```)");
match = regex.Match(str);
if (match.Success)
{
request = match.Groups["request"].Value;
}
else
{
msg = "获取请求响应失败";
return false;
}
return true;
}

概述

分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务。 该项目地址为:https://github.com/twitter/snowflake是用Scala实现的。

结构

snowflake的结构如下(每部分用-分开):

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

一共加起来刚好64位,为一个Long型。(转换成字符串长度为18)

snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。据说:snowflake每秒能够产生26万个ID。

C#代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class IdWorker
{
//机器ID
private static long workerId;
private static long twepoch = 687888001020L; //唯一时间,这是一个避免重复的随机量,自行设定不要大于当前时间戳
private static long sequence = 0L;
private static int workerIdBits = 4; //机器码字节数。4个字节用来保存机器码(定义为Long类型会出现,最大偏移64位,所以左移64位没有意义)
public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大机器ID
private static int sequenceBits = 10; //计数器字节数,10个字节用来保存计数码
private static int workerIdShift = sequenceBits; //机器码数据左移位数,就是后面计数器占用的位数
private static int timestampLeftShift = sequenceBits + workerIdBits; //时间戳左移动位数就是机器码和计数器总字节数
public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成
private long lastTimestamp = -1L;

/// <summary>
/// 机器码
/// </summary>
/// <param name="workerId"></param>
public IdWorker(long workerId)
{
if (workerId > maxWorkerId || workerId < 0)
throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0 ", workerId));
IdWorker.workerId = workerId;
}

public long nextId()
{
lock (this)
{
long timestamp = timeGen();
if (this.lastTimestamp == timestamp)
{ //同一微妙中生成ID
IdWorker.sequence = (IdWorker.sequence + 1) & IdWorker.sequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限
if (IdWorker.sequence == 0)
{
//一微妙内产生的ID计数已达上限,等待下一微妙
timestamp = tillNextMillis(this.lastTimestamp);
}
}
else
{ //不同微秒生成ID
IdWorker.sequence = 0; //计数清0
}
if (timestamp < lastTimestamp)
{ //如果当前时间戳比上一次生成ID时时间戳还小,抛出异常,因为不能保证现在生成的ID之前没有生成过
throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds",
this.lastTimestamp - timestamp));
}
this.lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳
long nextId = (timestamp - twepoch << timestampLeftShift) | IdWorker.workerId << IdWorker.workerIdShift | IdWorker.sequence;
return nextId;
}
}

/// <summary>
/// 获取下一微秒时间戳
/// </summary>
/// <param name="lastTimestamp"></param>
/// <returns></returns>
private long tillNextMillis(long lastTimestamp)
{
long timestamp = timeGen();
while (timestamp <= lastTimestamp)
{
timestamp = timeGen();
}
return timestamp;
}

/// <summary>
/// 生成当前时间戳
/// </summary>
/// <returns></returns>
private long timeGen()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
}
}

调用方法:

1
2
3
4
IdWorker idworker = new IdWorker(1); for (int i = 0; i < 1000; i++)
{
  Console.WriteLine(idworker.nextId());
}

https://www.cnblogs.com/lwqlun/p/12453593.html

在.NET生态系统中,控制台程序的表现相对较差。通常来说,这种项目经常作为Demo演示使用。现在是时候让控制台应用程序得到其应有的尊重了。

终端技术的发展开启了增强用户体验的复兴。 ITerm2, Hyper, Windows Terminal,所有这些工具都为单调的控制台体验增加了一些趣味。 虽然这些工具都允许用户定制自己体验,但是对于开发人员来说,他们还希望向控制台应用程序中添加一些编程风格。

在本篇博文中,我们将一起看一下如何使用一些出色的开源项目为我们的控制台程序增添趣味。这里说明的顺序并不表明项目的优劣,他们都是改善我们控制台程序体验的优秀方案。

Colorful.Console

Colorful.Console是一个Nuget包,它可以增强我们对控制台输出文字样式的控制。我们可以使用System.Drawing.Color中定义的颜色来定义控制台程序的配色方案。

1
2
3
4
5
6
using System;
using System.Drawing;
using Console = Colorful.Console;

Console.WriteLine("console in pink", Color.Pink);
Console.WriteLine("console in default");`

除此之外,Colorful.Console还允许我们使用FIGlet字体编写带颜色的ASCII码输出

FIGLet: http://www.figlet.org/

1
2
3
4
5
6
FigletFont font = FigletFont.Load("chunky.flf");
Figlet figlet = new Figlet(font);

Console.WriteLine(figlet.ToAscii("Belvedere"), ColorTranslator.FromHtml("#8AFFEF"));
Console.WriteLine(figlet.ToAscii("ice"), ColorTranslator.FromHtml("#FAD6FF"));
Console.WriteLine(figlet.ToAscii("cream."), ColorTranslator.FromHtml("#B8DBFF"));`

这个输出的结果完全就是黑客的梦想。

我建议你访问一下colorful.console的官方站点,了解这个库能实现的所有效果,以便更好的改善控制台程序的体验。

Colorful.Console: http://colorfulconsole.com/

ConsoleTables

ConsoleTables包是我(作者)自己编写的,这里有一点厚颜无耻^.^。 使用这个库,可以让开发人员很轻松的将一组对象以表格的形式展示在控制台中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void Main(String[] args)
{
var table = new ConsoleTable("one", "two", "three");
table.AddRow(1, 2, 3)
.AddRow("this line should be longer", "yes it is", "oh");

table.Write();
Console.WriteLine();

var rows = Enumerable.Repeat(new Something(), 10);

ConsoleTable
.From<Something>(rows)
.Configure(o => o.NumberAlignment = Alignment.Right)
.Write(Format.Alternative);

Console.ReadKey();
}

以前,谁不希望能在控制台中输出一个表格呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FORMAT: Default:

--------------------------------------------------
| one | two | three |
--------------------------------------------------
| 1 | 2 | 3 |
--------------------------------------------------
| this line should be longer | yes it is | oh |
--------------------------------------------------

Count: 2

FORMAT: Alternative:

+----------------------------+-----------+-------+
| one | two | three |
+----------------------------+-----------+-------+
| 1 | 2 | 3 |
+----------------------------+-----------+-------+
| this line should be longer | yes it is | oh |
+----------------------------+-----------+-------+`

自从ConsoleTables发布以来,许多开发人员已经研发出自己的控制台表格库了。有一些甚至更好,你可以自行去查找一下。

ShellProgressBar

和需要其他应用程序一样,控制台程序也可以执行长时任务。ShellProgressBar是一个非常棒的库,使用它,你可以在控制台输出一些非常惊艳的进度条。而且,ShellProgressBar是可以实现进度条的嵌套使用。例如,如下GIF动画中展示的效果。

ShellProgressBar使用起来相当的直接。

1
2
3
4
5
6
7
8
9
10
11
12
const int totalTicks = 10;
var options = new ProgressBarOptions
{
ProgressCharacter = '─',
ProgressBarOnBottom = true
};
using (var pbar = new ProgressBar(totalTicks, "Initial message", options))
{
pbar.Tick(); //will advance pbar to 1 out of 10.
//we can also advance and update the progressbar text
pbar.Tick("Step 2 of 10");
}

谢谢你, Martijin Larrman, 这真的是一个非常好用的库。

GUI.CS

GUI.CS是一个非常棒的控制台UI工具包。它提供了一个功能完善的工具箱,开发人员可以使用它构建早期控制台常见的一种用户界面。

这个UI工具箱提供了如下控件:

  • Buttons
  • Labels
  • Text Entry
  • Text View
  • User Inputs
  • Windows
  • Menus
  • ScrollBars

使用它,开发人员可以在控制台应用中实现一些令人难以置信的效果。这个库是由Miguel De Icaza编写的,是控制台技术的巅峰之作,下面让我们一起来看一个实例程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using Terminal.Gui;

class Demo {
static void Main ()
{
Application.Init ();
var top = Application.Top;

// 创建顶级窗体
var win = new Window ("MyApp") {
X = 0,
Y = 1, // 预留菜单行

// 使用Dim.Fill(), 它可以自动调整窗体大小,实现自适应,而无需手动敢于
Width = Dim.Fill (),
Height = Dim.Fill ()
};
top.Add (win);

// 创建一个菜单
var menu = new MenuBar (new MenuBarItem [] {
new MenuBarItem ("_File", new MenuItem [] {
new MenuItem ("_New", "Creates new file", NewFile),
new MenuItem ("_Close", "", () => Close ()),
new MenuItem ("_Quit", "", () => { if (Quit ()) top.Running = false; })
}),
new MenuBarItem ("_Edit", new MenuItem [] {
new MenuItem ("_Copy", "", null),
new MenuItem ("C_ut", "", null),
new MenuItem ("_Paste", "", null)
})
});
top.Add (menu);

var login = new Label ("Login: ") { X = 3, Y = 2 };
var password = new Label ("Password: ") {
X = Pos.Left (login),
Y = Pos.Top (login) + 1
};
var loginText = new TextField ("") {
X = Pos.Right (password),
Y = Pos.Top (login),
Width = 40
};
var passText = new TextField ("") {
Secret = true,
X = Pos.Left (loginText),
Y = Pos.Top (password),
Width = Dim.Width (loginText)
};

// 添加一些其他控件
win.Add (
// 这是我最喜欢的布局
login, password, loginText, passText,

// 这里使用了绝对定位
new CheckBox (3, 6, "Remember me"),
new RadioGroup (3, 8, new [] { "_Personal", "_Company" }),
new Button (3, 14, "Ok"),
new Button (10, 14, "Cancel"),
new Label (3, 18, "Press F9 or ESC plus 9 to activate the menubar"));

Application.Run ();
}
}

总结

作为开发人员,我们可以沉迷于GUI, 这是理所当然的,它使我们更有生产力。但是控制台应用程序同样也很强大。下次当你编写控制台程序的时候,你可以考虑使用以上介绍的某些库,以便为你的控制台应用增添色彩。