在shell中使用正则表达式

引言

本篇文章主要是针对shell中的 grepegrep 的讲解,对于正则表达式的规则本身并没有太多的内容。在阅读本篇文章之前,读者应该对正则表达式有个大致的了解。

grep 命令

grep 命令是linux终端中最常用的命令之一,它的全称是“global regular expression print”,从字面上理解, grep 可以用来进行正则表达式匹配。

常用选项
使用一些选项命令能够更加方便地进行匹配
-i 忽略大小写
-v 进行逆向匹配
-n 打印行号
--color=auto 彩色输出结果

支持的表达式
正则表达式有很多规则, grep 支持的有如下:
在每一行的开始

grep "^GUN" GPL-3

在每一行的结尾

grep "and$" GPL-3

匹配任意字符

grep "..cept" GPL-3

在指定的字符中进行匹配

grep "t[wo]o" GPL-3

在指定的字符中进行逆向匹配

grep "[^c]ode" GPL-3

使用范围来指定字符

grep "^[A-Z]" GPL-3

限定出现的次数为0次或0次以上

grep "([A-Za-z ]*)" GPL-3

egrep 命令

egrep 的含义为“extended grep”,相对于 grep 有更多的正则表达式特性。同样,可以在使用 grep 时增加 -E 参数获得相同的效果。

表达式分组
使用括号能够将表达式进行分组,下面的任何一个都能够实现这个效果

grep "\(grouping\)" file.txt
grep -E "(grouping)" file.txt
egrep "(grouping)" file.txt

表达式进行中多选一

egrep "(GPL|General Public License)" GPL-3

匹配的重复次数为0次或0次以上,与 * 限定符类似,但是 ? 可以作用于一组字符

egrep "(copy)?right" GPL-3

指定匹配的重复次数

egrep "[AEIOUaeiou]{3}" GPL-3

线程与进程的比较

进程与线程之间有什么区别

进程(process)是指在系统中正在运行的一个应用程序,是系统资源分配的基本单位,在内存中有其完备的数据空间和代码空间,拥有完整的虚拟空间地址。一个进程所拥有的数据和变量只属于它自己。

线程(thread)是进程内相对独立的可执行单元,所以也被称为轻量进程(lightweight processes)。线程是操作系统进行任务调度的基本单元,它与父进程的其它线程共享该进程所拥有的全部代码空间和全局变量,但拥有独立的堆栈(即局部变量对于线程来说是私有的)。

进程和线程都具有就绪、阻塞和运行三种基本状态。

联系

  • 一个进程至少拥有一个线程(主线程),也可以拥有多个线程
  • 一个线程必须有一个父进程,多个进程可以并发执行
  • 一个线程可以创建和撤销另一个线程
  • 同一个进程中的多个线程之间可以并发执行

区别

系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销
资源管理:进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

通信方式

进程间通信主要包括管道、系统IPC(包括消息队列,信号量,共享存储)、SOCKET,具体说明参考linux进程间通信方式。进程间通信其实是指分属于不同进程的线程之间的通讯,所以进程间的通信方法同样适用于线程间的通信。但对于同一进程的不同线程来说,使用全局变量进行通信效率会更高。

什么情况下适合使用线程

线程相对于进程的优势:

  • 线程间通信(数据交互)比进程间通信更加简便快捷。
  • 线程之间的上下文切换速度要比进程间的快,也就是说,对于操作系统而言,停止一个线程并启动另一个线程的速度比进程之间相似操作更快。
  • 创建一个线程的速度比创建一个进程的速度快。

下面的一些情景更适合使用线程来完成:

  • 进行耗时较长的处理:当一个windows界面程序正在计算时,它就不能处理其他消息(message)了,界面就不能及时更新。
  • 进行后台处理:有些任务并不是时间紧迫的,但是需要定期执行。
  • 进行 I/O 处理: I/O 操作或网络操作一般都会有一定的延迟,线程能保证程序中的其他部分不会受到延迟的影响。

什么情况下适合使用进程

进程相对于线程的优势:

  • 多线程系单进程的软件只能运行在一台机器上,而多进程的软件可以运行在多台机器上。这在一定程度上限制软件的可拓展性,单台机器的性能总是有限的。
  • 多进程模型的鲁棒性更好,有更好的容错性。运行在同一进程下的多个线程会相互影响,如果其中的一个线程出了问题,可能会波及整个进程。
  • 有些功能模块并不是线程安全的,这时就只能使用多进程模型了。

以chrome浏览器为例,它使用了多进程模型。每一个标签页都是一个进程,这样能够将渲染引擎中的bug隔离在该进程内,从而使整个程序免受影响。多进程模型将JavaScript隔离在每个进程中,这样就能防止其因占用过多的CPU或内存,使整个软件失去响应。另一方面,多进程模型的系统开销也是比较大的,这点能够从chrome的高额内存使用中看出来。不过chrome的优化策略可以让较低内存的情况下有更好内存使用效率。当一个标签页处于非激活状态,其对应的内存可以在需要时被交换进硬盘中,这样用户当前操作的页面就能够保持响应状态。如果使用的是多线程模型,区分活跃内存和非活跃内存就变得十分困难,导致内存使用效率的下降。

静态成员变量初始化相关问题

引言

这篇文章的起因是出于对于 C++ 饿汉单例模式代码的一些疑问,查阅了一些资料。在仔细研究后,我发现在一些基础概念的理解还是存在偏差。
下面请看这样的一段代码,能看出其中有那些不太“正常”的语句么。

class Singleton
{
public:
    static Singleton* getInstance();
private:
    Singleton() {}
    static Singleton* instance;
};

Singleton* Singleton::instance = new Singleton();
Singleton* Singleton::getInstance() {
    return instance;
}

私有静态成员的初始化

上面的代码是饿汉单例模式的 C++ 的实现,在没有查阅资料之前,我对其中私有静态成员变量的初始化存疑。主要有以下两点:

  • 为什么私有变量能够在类外被修改
  • 为什么私有构造函数能够在类外被调用

在我之前的知识积累中, 私有的成员变量或成员函数是不能够在类外被访问的 。那么为什么以上代码没有问题呢?

在C++标准中找到了下面的一段话(可以在 C++11 standard 的 9.4.2节 或 C++ standard working draft 的 10.3.9.2节 中找到)

The initializer expression in the definition of a static data member is in the scope of its class.

这句话的意思是:静态成员变量的初始化是被看做为它自身的类域中的( 翻译的可能不是很准 )。这样就不难理解为什么私有的静态成员变量能够在其类外被初始化了,由其私有构造函数进行构造也能说的通了。

同样 ,在C++标准中给出了下面这样的示例代码:

class process {
  static process* run_chain;
  static process* running;
};

process* process::running = get_main();
process* process::run_chain = running;

给出的说明如下:

The static data member run_chain of class process is defined in global scope; the notation process​::​run_chain specifies that the member run_chain is a member of class process and in the scope of class process. In the static data member definition, the initializer expression refers to the static data member running of class process.

静态成员 run_chain 定义在全局域;而 process::run_chain 则表示 run_chainprocess 类的成员变量,从而处在 process 类的作用域中。

私有构造函数

在查阅资料时,我发现 Peter 的 描述 纠正了我对私有构造函数的一些看法。

The point of a private constructor is not preventing object construction. It is about controlling which code can access the constructor, and therefore limiting which code to create an object that is an instance of that class. A private constructor is accessible to all member functions ( static or otherwise) of the class, and to all declared friend s of the class (which may be individual functions, or other classes) - so any of those can create an instance of the class using a private constructor (assuming the constructor is defined).

私有构造函数的目的并不是禁止对象构造,其目的在于控制哪些代码能够调用这个构造函数,进而限制类对象的创建。私有的构造函数可以被该类的所有成员函数(静态或非静态的)调用,该类的友元类或友元方法也能访问该类的私有函数,所以在上述情况中都可以通过私有的构造函数实例化出类对象。

客户端使用非阻塞socket进行connect的流程

问题

使用非阻塞( non-blocking ) socket尝试与服务端建立连接( connect )时,由于是io非阻塞的,所以 connect 函数会立即返回,那么如何判断client与server连接成功了呢?

解答

客户端建立连接的示例代码如下:

int res = connect(fd, ...);
if (res < 0 && errno != EINPROGRESS) {
    // case1. error, fail somehow, close socket
    return;
}

if (res == 0) {
    // case2. connection has succeeded immediately
} else {
    // case3. connection attempt is in progress
}

由于是非阻塞模式,所以 connect 之后会直接返回,根据返回值 reserrno 能够判断建立连接的结果。

  • case1,表示连接失败;
  • case2,表示连接建立成功;
  • case3,表示正在建立连接的过程中,在这个情况下,需要等待socket变成可写(writable)状态,可以使用 selectepoll 完成;

在 case3 情况下,socket可写后,执行下面的代码检查socket是否出现错误。

  • case4和case5,表示socket出现了错误,将会关闭连接;
  • case6,表示连接建立成功,可以开始 readwrite 了。
int result;
socklen_t result_len = sizeof(result);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &result, &result_len) < 0) {
    // case4. error, fail somehow, close socket
    return;
}

if (result != 0) {
    // case5. connection failed; error code is in 'result'
    return;
}

// case6. socket is ready for read()/write()

参考资料

为什么要使用各种前端框架

引言

在学习前端的过程中,我开始接触到许多开发框架或开发工具,这些工具让我开始眼花缭乱。使用常规的html/css/js就已经能够开发出这种网页了,所以我问自己,为什么要学习这些框架或工具,这些库是用来解决什么问题的。想明白这个问题,我学习的目的就更加明确,在遇到一些开发情景时,也能够快速准确地挑选出应该使用的框架。

下面按照我目前的理解,对目前的主流的前端开发库进行一些分析和比较。由于我还是一个初学着,所以我的观点可能还不是很成熟。

为什么要使用jQuery

jQuery是一个轻量级js库,它有如下特性:

  • 简化了HTML/DOM操作
  • CSS选择器
  • 动画特效支持
  • 解决跨浏览器的兼容行问题
  • 完善的Ajax支持

在查阅资料的过程中,我还听到一些主张停用jQuery的声音。原因就在于随着前端开发生态不断的完善,现代浏览器原生API已经足够好用。

为什么要使用SASS

SASS是一种CSS预编译格式,它最终会编译成CSS。
SASS提供了CSS语法的拓展,它支持变量,算数运算符,循环操作,函数操作,样式模板(mixin)等强大的特性。它能够让开发人员更方便地编写CSS。SASS支持 import 关键字,这样就能够很方便地对样式进行模块化设计。

加一句题外话,在使用SASS时,需要考虑的另一个问题就是代码物理结构的安排(直白的说就是目录的划分,源文件的存放位置等),这也是所有代码开发中需要注意的问题。

与SASS相似的工具还有LESS。

为什么要使用Bootstrap

Bootstrap是一个用于快速开发网页的前端框架。以我目前的理解来看,它更像是一个网页样式框架。

  • 丰富的基础样式和资源
  • 响应式(responsive),良好的移动端支持
  • 强大的网格(grid)系统
  • 一致性,保证跨浏览器的样式一致

为什么要使用React

DOM操作对性能的影响很大,频繁的DOM操作会使网页渲染的速度变慢,React主要解决了这个问题。
React.js允许在js中编写html,形成Virtual DOM,只在变化进行重新渲染。

下面的一段文字摘自参考资料中的第5篇文章。

  • Am I building an app which has lots of DOM interactions, or is it a relatively simple app with relatively few interactions?
  • Does my app require a very high browser performance, that any user interactions must immediately be reflected in the UI? Is my data going to change over time, at a high rate?
  • Is one user interaction going to affect many other areas/components in the UI?
  • Is there a possibility that my app could grow big in the future, and so I would want to write extremely neat, modular front-end JavaScript that is easy to maintain? Would I want to use lots of native JavaScript or would I prefer using a lot of abstractions/methods provided in a framework?
  • Would I prefer a framework/library which does not have a high learning curve and which is pretty easy to get started with development?

在使用React之前,问自己这样几个问题,就能够帮助你决定是否需要使用React。

  • 所开发的网页应用涉及到很多DOM操作么?
  • 开发的网页应用需要很高的性能么,这意味着任何用于的操作都要立刻反应在界面上?我的数据变更会很频繁么?
  • 我开发的应用会在未来逐渐变得复杂么,这样我就需要将前端的js代码模块化?我是否会使用许多原生js特性,是否虚幻使用框架提供的抽象方法
  • 是否需要一个学习曲线比较平缓的框架,能够方便地上手进行开发?

javscript中的OOP特性

1 引言

js也是具有OOP(object oriented programming)特性的,OOP在构建一些大型应用程序还是有一套成熟理论的。作为C++的使用者在学习js中的OOP特性的过程中,能够较快地理解其中的各种术语和概念,也能比较两种语言的异同,深化知识理解。通过js的OOP特性的学习也让我开始从语言层面考虑程序设计问题。
本篇文章主要介绍了js中的一些OOP特性,并且比较了js与C++的语言特性。如果你能熟练掌握C++的OOP特性,本文能帮助你快速地对js中的OOP特性建立整体的认识。

2 写给C++使用者的js中的OOP特性介绍

2.1 创建对象(object)

js中创建object的代码,示例如下:

let duck = {
  name: "Aflac",
  numLegs: 2,
  sayName: function() {
    return "The name of this duck is " + this.name + ".";
  }
};

js直接通过 {} 就可以创建出对象示例来,不需要对该对象(object)的类(class)进行声明。这点和C++不是很相同,C++需要先声明一个class再创建object。
这个object有两个成员变量和一个成员函数,需要注意的是这两个成员变量都是公有(public)的,他们是可以直接用 . 符号访问的。
js中也有 this 关键字,与C++相同, this 关键字用于表示当前实例。

2.2 类(class)的声明

js中声明一个类的操作实际上就是声明一个构造函数。

let Bird = function(name, color) {
  this.name = name;
  this.color = color;
  this.numLegs = 2;
}

let crow = new Bird("Alexis", "black");

crow instanceof Bird; // => true

上面的代码声明了Bird类,在js中通常类的名字都是由首字母大写的单词表示的。类的构造函数也能接受参数用于对实例的初始化,这点与C++非常相似,使用 new 关键字就能够创建该类的实例。
使用 instanceof 关键字用于检查对象是否属于某个类,也可通过验证 constructor 属性来判断一个对象是否属于一个类 crow.constructor == Bird

2.3 类的共有成员

js中通过 prototype 这一属性(把它叫做关键字好像还不太合适)能够实现C++中静态成员变量和静态成员函数的特性。

Bird.prototype.numLegs = 2;

上面的代码就给Bird类增加了一个静态成员变量。这个 prototype 可以是一个对象,这样类的共有成员就能方便地承载更多的属性了,示例代码如下。

Bird.prototype = {
  constructor: Bird,
  numLegs: 2,
  eat: function() {
    console.log("nom nom nom");
  },
  describe: function() {
    console.log("My name is " + this.name);
  }
};

需要注意的是需要设置好 constructor 属性,这样是为了保证代码逻辑的一致性。
对象会获得类的 prototype 属性,可以通过 isPrototypeof 方法来验证。

Bird.prototype.isPrototypeOf(duck);

2.4 类的私有成员

js与C++一样,也可以有私有成员变量,代码如下所示。 hatchedEgg 就相当与是Bird的私有成员变量,并且提供了修改这个成员变量的方法 getHatchedEggCount

function Bird() {
  let hatchedEgg = 10; // private property

  this.getHatchedEggCount = function() {
    // publicly available method that a bird object can use
    return hatchedEgg;
  };
}
let ducky = new Bird();
ducky.getHatchedEggCount(); // returns 10

这种形式在js中被称作闭包(closure),函数能够访问到与他处在同一个作用域(context)中的变量。

2.5 类的继承和派生

js中的派生主要通过 prototype 体现,下面的代码表示Bird派生自Object。同样,需要注意将 constructor 属性设置好。

Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;

2.6 类的覆盖

js中可以重写基类中的方法,代码如下所示,这点与C++中的 override 相同。

function Animal() { }
Animal.prototype.eat = function() {
  return "nom nom nom";
};
function Bird() { }

// Inherit all methods from Animal
Bird.prototype = Object.create(Animal.prototype);

// Bird.eat() overrides Animal.eat()
Bird.prototype.eat = function() {
  return "peck peck peck";
};

Bird.prototype.fly = function() {
  console.log("I'm flying!");
};

通样也在派生之后也可以通过修改派生类的 prototype 以达到特化派生类的作用,上面的 fly 方法就是在Bird完成派生之后新增的方法。现在Bird有两个方法,它们分别是 eatfly

正则表达式备忘

引言

近在FreeCodeCamp(FCC)学习前端的课程,其中有一节就是讲的 regexp ,也就是正则表达式,之前我零星地了解过一些正则表达式的相关知识,不足以对正则表达式得出一个完整的认识。FCC的教程循序渐进,由简到繁,在教学中结合练习,非常适合初学者的学习过程。如何你的英文能够达到阅读技术类文献的水平,又正好想学习一下正则表达式,我推荐你试一下FCC的正则表达式教程,花3~4个小时就能够对正则表达式有个完整的理解。

本篇文章主要是对学习过程中的知识点进行总结,方便以后进行查阅。

要点总结(js版)

FCC的教程使用javascript作为开发语言,本节所有的代码都是js代码。

js中内置了正则表达式模块,下面是正则表达式在js中的使用示例

let myString = "Hello World";
let myRegex = /Hello/;
let result = myRegex.test(myString);

使用 | 进行or匹配

let regex2 = /cat|bird/;

使用 i 标识符在匹配时忽略大小写

let regex3 = /case/i;

使用正则表达式进行字符串过滤,使用 match 方法

let extractStr = "Extract the word 'coding' from this string.";
let codingRegex = /coding/; // Change this line
let result = extractStr.match(codingRegex); // Change this line

使用 g 进行返回满足规则的多次结果, g 为global的缩写

let twinkleStar = "Twinkle, twinkle, little star";
let starRegex = /twinkle/gi; // Change this line
let result = twinkleStar; // Change this line

使用 . 匹配所有的字符,使用 [] 匹配指定的字符

let quoteSample = "Beware of bugs in the above code; I have only proved it correct, not tried it.";
let vowelRegex = /[aeiou]/gi; // Change this line
let result = quoteSample.match(vowelRegex); // Change this line

[] 中可以使用 - 代表一个范围

let quoteSample2 = "The quick brown fox jumps over the lazy dog.";
let alphabetRegex = /[a-z]/gi; // Change this line
let result = quoteSample2.match(alphabetRegex); // Change this line

这个范围还可以是多个,并且允许包含数字

let quoteSample3 = "Blueberry 3.141592653s are delicious.";
let myRegex = /[h-s2-6]/gi; // Change this line
let result = quoteSample3.match(myRegex); // Change this line

使用 ^ 符号能够匹配不包含某些字符

let quoteSample4 = "3 blind mice.";
let myRegex2 = /[^0-9aeiou]/gi; // Change this line
let  result = quoteSample4.match(myRegex2); // Change this line

使用 + 对出现连续出现的字符进行匹配

let difficultSpelling = "Mississippi";
let  myRegex3 = /s+/g; // Change this line
let result = difficultSpelling.match(myRegex3);

使用 * 星号匹配可能没有出现的次数(出现次数为 0~n 次)

let chewieQuote = "Aaaaaaaaaaaaaaaarrrgh!";
let chewieRegex = /Aa*/; // Change this line
let result = chewieQuote.match(chewieRegex);

使用 ? 进行较短匹配 (lazy match, 与greedy match 对应)

let text = "<h1>Winter is coming</h1>";
let myRegex = /<.*?>/; // Change this line
let result = text.match(myRegex);

使用 ^ 对出现在字符串开始位置进行匹配
注意上边也用到这个符号表示取反,是在 [] 中表示取反

let rickyAndCal = "Cal and Ricky both like racing.";
let calRegex = /^Cal/; // Change this line
let result = calRegex.test(rickyAndCal);

使用 $ 对出现在字符串结束位置进行匹配

let caboose = "The last car on a train is the caboose";
let lastRegex = /caboose$/; // Change this line
let result = lastRegex.test(caboose);

使用 \w 代替 [A-Za-z0-9_] ,包含所有的数字、字母和下划线

let quoteSample = "The five boxing wizards jump quickly.";
let alphabetRegexV2 = /\w/g; // Change this line
let result = quoteSample.match(alphabetRegexV2).length;

使用 \W 代替 [^A-Za-z0-9_] ,相当于上边的逆向匹配

let quoteSample = "The five boxing wizards jump quickly.";
let nonAlphabetRegex = /\W/g; // Change this line
let result = quoteSample.match(nonAlphabetRegex).length;

使用 \d 代替 [0-9] ,所有数字匹配

let numString = "Your sandwich will be $5.00";
let numRegex = /\d/g; // Change this line
let result = numString.match(numRegex).length;

使用 \D 代替 [^0-9] ,所有非数字匹配

let numString = "Your sandwich will be $5.00";
let noNumRegex = /\D/g; // Change this line
let result = numString.match(noNumRegex).length;

使用 \s 代替 [ \r\t\f\n\v] ,与空格和换行相关

let sample = "Whitespace is important in separating words";
let countWhiteSpace = /\s/g; // Change this line
let result = sample.match(countWhiteSpace);

使用 \S=,代替 =[^ \r\t\f\n\v] ,不包含空格和换行

let sample = "Whitespace is important in separating words";
let countNonWhiteSpace = /\S/g; // Change this line
let result = sample.match(countNonWhiteSpace);

使用 {min, max} 对字符出现的次数进行限定
回想起使用 + 限制次数为 {0,} ,使用 - 限制次数为 {1,}

let ohStr = "Ohhh no";
let ohRegex = /Oh{3,6} no/; // Change this line
let result = ohRegex.test(ohStr);

仅限制出现次数的下限 {min,}

let haStr = "Hazzzzah";
let haRegex = /Haz{4,}ah/; // Change this line
let result = haRegex.test(haStr);

指定出现的次数 {count}

let timStr = "Timmmmber";
let timRegex = /Tim{4}ber/; // Change this line
let result = timRegex.test(timStr);

使用 ? 匹配可能出现的字母
问号用于声明lazy match,如何判断是lazy match还是匹配可能出现的东西呢

let favWord = "favorite";
let favRegex = /favou?rite/; // Change this line
let result = favRegex.test(favWord);

lookahead 的概念, (?=...)(?!...)

下面的正则表达式用于检查密码,由3~6个字母和至少1个数字组成

let password = "abc123";
let checkPass = /(?=\w{3,6})(?=\D*\d)/;
let result = checkPass.test(password); // Returns true

下面的正则表达式用于检查密码,有5个以上的字符和至少2个数字组成

let sampleWord = "astronaut";
let pwRegex = /(?=\w{5,})(?=\D*\d{2,})/; // Change this line
let result = pwRegex.test(sampleWord);

截取字符串中的重复部分,使用括号 () 对内容进行补充,是用 \1 表示第一个捕捉到的字符

let repeatNum = "42 42 42";
let reRegex = /^(\d*)\s\1\s\1$/; // Change this line
let result = reRegex.test(repeatNum);

通过截取字符串,可以对截取的字符串进行修改或替换操作,需要使用 replace 方法

"Code Camp".replace(/(\w+)\s(\w+)/, '$2 $1'); // Returns "Camp Code"

下面的正则表达式就是用于将 good 替换为 okey-dokey

let huhText = "This sandwich is good.";
let fixRegex = /good/; // Change this line
let replaceText = "okey-dokey"; // Change this line
let result = huhText.replace(fixRegex, replaceText);

使用正则表达式实现类似 .trim() 的功能

let hello = "   Hello, World!  ";
let wsRegex = /^\s+|\s+$/g; // Change this line
let result = hello.replace(wsRegex, ''); // Change this line

elisp和python中的正则表达式

在emacs中内置了 string-match 函数进行正则表达式匹配,其函数原型为 (string-match REGEXP STRING &optional START) ,下面就是在emacs中使用regexp的示例。

(setq str-regexp "\.[h|cpp]$")
(setq str-sample-1 "sample1.h")
(string-match str-regexp str-sample-1)

python中内置了 re 模块用于正则表达式匹配,下面就是在python中的regexp示例。

import re
pattern = r"Cookie"
sequence = "Cookie"
if re.match(pattern, sequence):
    print("Match!")
else:
    print("Not a match!")