单元测试浅析

单元测试浅析 1、何为单元测试 在计算机编程中,单元测试(英语:[[Unit Testing]])又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。 2、单元测试的优势 - 适应变更 单元测试允许程序员在未来重构代码,并且确保模块依然工作正确(复合测试)。这个过程就是为所有函数和方法编写单元测试,一旦变更导致错误发生,借助于单元测试可以快速定位并修复错误。 可读性强的单元测试可以使程序员方便地检查代码片断是否依然正常工作。良好设计的单元测试案例覆盖程序单元分支和循环条件的所有路径。 - 简化集成 单元测试消除程序单元的不可靠,采用自底向上的测试路径。通过先测试程序部件再测试部件组装,使集成测试变得更加简单。 - 文档记录 单元测试提供了系统的一种文档记录。借助于查看单元测试提供的功能和单元测试中如何使用程序单元,开发人员可以直观的理解程序单元的基础 API。 单元测试具体表现了程序单元成功的关键特点。这些特点可以指出正确使用和非正确使用程序单元,也能指出需要捕获的程序单元的负面表现(译注:异常和错误)。尽管很多软件开发环境不仅依赖于代码做为产品文档,在单元测试中和单元测试本身确实文档化了程序单元的上述关键特点。 - 表达设计 在测试驱动开发的软件实践中,单元测试可以取代正式的设计。每一个单元测试案例均可以视为一项类、方法和待观察行为等设计元素。 3、单元测试的局限 测试不可能发现所有的程序错误,单元测试也不例外。按定义,单元测试只测试程序单元自身的功能。因此,它不能发现集成错误、性能问题、或者其他系统级别的问题。单元测试结合其他软件测试活动更为有效。与其它形式的软件测试类似,单元测试只能表明测到的问题,不能表明不存在未测试到的错误。 4、单元测试的要求 4.1 代码可测性 这种情况可能是你代码本身导致的,首先你要写具有“可测性”的代码,这意味着你不能写面向过程的,流水式的,几百行逻辑堆一起的代码(也叫意大利面代码,就像一盘意大利面一样搅在一起的代码。),你要学一些模块化技巧,面向对象和函数式编程理念,还有很多其它具体方法,比如能用本地变量,就不要用全局变量等等,让你的代码具有可测性,这些知识的学习应该放在单元测试之前。 4.2 测试独立性 每个理想的测试案例独立于其它案例;为测试时隔离模块,经常使用 stubs、mock 或 fake 等测试马甲程序。不与外部(包括文件、数据库、网络、外部服务)直接相依。 4.3 测试有效性 测试代码和功能代码同时提交。 4.4 测试及时性 单元测试的目的是,针对修改的每个函数或方法,进行快速的测试,观察结果是否如预期。不说代码改动的效果如何(性能,规范等),至少可以正常运行。 4.5 一个测试案例只测一个方法 单元测试注重快速,小而美,针对一个实例方法,可能会有很多不同方向的测试,将这些测试方法分开。假设测试账户有效性,账户有效写个方法,账户无效写个方法。 5、单元测试之stub,mock,fake 这三个概念来源自面向对象设计语言在 TDD 方法下的实践。 stub 检测回调值return。我们用 pytest 或者 unittest 替代。 mock 模拟和外界的连接。我们用 python mock 替代。 fake 虚拟环境。我们可以显式修改临时变量。 6、python 常见单元测试框架 6.1 unittest python 标准库自带的单元测试框架。 6.2 pytest 非常简洁好用的单元测试框架。简单使用方法可以参加博客pytest的简单学习 6.3 mock 单元测试应该只针对当前单元进行测试, 所有的内部或外部的依赖应该是稳定的, 已经在别处进行测试过的。 ...

单链表的Python实现(列表实现)

Python Data Structure 单链表的 Python 实现 一、节点 节点,即 C 语言里使用 struct 语句定义的一种非基础数据类型,在 Python 中,定义一个class 类。 class Node(object): def __init__(self, data, next=None): """包含一个数据域,一个 next 域的节点,next 是对下一个数据的引用 """ self.data = data self.next = next class TwoWayNode(Node): """继承 Node 类的双指针节点类,新包含一个 previous 的引用 """ def __init__(self, data, previous=None, next=None): Node.__init__(self, data, next) self.previous = previous def main(): pass if __name__ == "__main__": main() 二、单链表 单链表,指的是由多个节点形成的链式结构,一个节点包括一个数据域及一个 next 域。 除头结点外,每个节点都有且仅有一个前驱;除尾节点外,每个节点有且仅有一个后继。 ADT 思想(忽略)。 定义一个单链表类,依次给出以下的方法: 1、 在首部插入节点 2、在尾部插入节点 3、在任意位置插入节点 4、从首部删除节点 5、从尾部删除节点 6、从任意位置删除节点 7、遍历输出 8、根据数值查找单链表中是否存在项,返回 True or False 及索引位置 9、根据给出的索引替换掉相应位置的值 from node import Node class SingleList(object): """以节点的方式实现链表,默认实现空列表 """ def __init__(self): self.head = None # 在开始处插入 def add_node_first(self, data): self.head = Node(data, self.head) # 在末尾插入,若为空列表则直接插入,否则遍历到尾部 def add_node_last(self, data): new_node = Node(data) if self.head is None: self.head = new_node else: probe = self.head while probe.next is not None: probe = probe.next probe.next = new_node # 任意位置添加节点,若 index 小于零,加在首部,若 index 大于链表长度,加在尾部 def add_node_anywhere(self, index, data): if self.head is None or index <= 0: self.head = Node(data, self.head) else: probe = self.head while index >1 and probe.next is not None: probe = probe.next index -= 1 probe.next = Node(data, probe.next) # 从首部删除节点 def pop_node_first(self): if self.head is not None: removed_item = self.head.data self.head = self.head.next return removed_item else: return -1 # 从尾部删除节点 def pop_node_last(self): if self.head is None: return -1 elif self.head.next is None: removed_item = self.head.data self.head = None else: probe = self.head while probe.next.next is not None: probe = probe.next removed_item = probe.next.data probe.next = None return removed_item # 任意位置删除节点,若 index 小于零,则删除首部节点,若 index 大于链表长度,则删除尾部节点 def pop_node_anywhere(self, index): if index <= 0 or self.head.next is None: removed_item = self.head.data self.head = self.head.next return removed_item else: probe = self.head while index > 1 and probe.next.next is not None: probe = probe.next index -= 1 removed_item = probe.next.data probe.next = probe.next.next return removed_item # 遍历输出 def traverse(self): probe = self.head while probe is not None: print(probe.data) probe = probe.next # 根据数值查找链表有没有该数据 def search(self, data): probe = self.head cnt = 0 while probe is not None and data != probe.data: probe = probe.next cnt += 1 if probe is not None: return "In", cnt else: return "Not in", -1 # 根据索引位置替换数据 def replace(self, index, new_data): probe = self.head while index > 0 and probe is not None: probe = probe.next index -= 1 if probe is None: return -1 else: probe.data = new_data return "Done" # 测试用例 a = SingleList() a.add_node_first(1) a.add_node_first(2) a.add_node_first(3) print(a.search(2)) a.add_node_first(4) a.add_node_last(5) a.add_node_first(6) a.add_node_anywhere(100,33) a.pop_node_anywhere(2) a.traverse() print("--------")

常见排序算法的 Python 实现

Python [[Algorithm]] 常见排序算法的 Python 实现 选择排序 """ 选择排序,顾名思义,扫描全表,选最小值 外循环走size-1次,每一次确定当前一个最小的数 内循环走size-(已经确定的最小数),理解为当前位置之前的数字都已有序,从当前位置出发到结尾扫描确定当前最小的数字 时间复杂度:平均O(n**2),最坏O(n**2),最好O(n**2) 空间复杂度:O(1) 稳定性:不稳定 """ def select_sort(sort_list): for i in range(size-1): min = i for j in range(i, size): if sort_list[j] < sort_list[min]: min = j sort_list[min], sort_list[i] = sort_list[i], sort_list[min] print("选择排序结果是:",sort_list) 冒泡排序 """ 冒泡排序,逐个比较,将最大的数排到最后方 外循环走size-1次,每次确定一个最大的数 内循环走size-(当前已确定的数),理解为从头开始,两两比较,a(n)>a(n+1),则交换 时间复杂度:平均O(n**2),最坏O(n**2),最好O(n) 空间复杂度:O(1) 稳定性:稳定 """ def bub_sort(sort_list): for i in range(size-1): for j in range(1, size): if sort_list[j] < sort_list[j-1]: sort_list[j], sort_list[j-1] = sort_list[j-1], sort_list[j] print("冒泡排序结果是:",sort_list) 插入排序 """ 插入排序,类似于体育课排队列 外循环走size-1次,每次确定一个较小的数,一次内循环结束,当前位置的左侧是相对大小确定的 内循环走0次或者当前已确定数的次数,理解为当前数与之前的第一个数对比,若小于则交换,继而继续比较,所以最少0次,最多当前已确定数次 时间复杂度:平均O(n**2),最坏O(n**2),最好O(n) 空间复杂度:O(1) 稳定性:稳定 """ def insert_sort(sort_list): for i in range(1, size): j = i while j > 0 and sort_list[j]<sort_list[j-1]: sort_list[j], sort_list[j-1] = sort_list[j-1], sort_list[j] j -= 1 print("插入排序结果是:",sort_list) 希尔排序 """ 希尔排序(该处指配合插入排序的希尔排序),由插入排序的定义可以看出来,当前的数想要确定位置必须与之前的数字逐个比较, 而希尔排序改成h个比较,这样做的好处是,针对数据量大的数组,排序的过程更轻松(构建h个不同的子数组,每个子数组逻辑相邻(相差距离为h)) 外循环的运算次数为(size = size//3循环,直到size等于1,每循环一次,运算次数加一 如size = 150,150//3=50(1次),50//3=16(2次),16//3=5(3次),5//3=1(4次)) 内循环为选择插入排序,次数由当前的外循环变量决定 时间复杂度:平均O(n**1.3),其他情况不好分析 空间复杂度:O(1) 稳定性:不稳定 """ def shell_sort(sort_list): h = 1 while h < size//3: h = h*3+1 while h >=1: for i in range(h, size): j = i while j > h and sort_list[j]<sort_list[j-h]: sort_list[j], sort_list[j-h] = sort_list[j-h], sort_list[j] j -= h h = h//3 print("希尔排序结果是:",sort_list) 归并排序 """ 归并排序,分治算法思想,将一个大问题分解成若干个小问题,若问题类似可用递归完成 常见两种归并算法,自顶向下和自底向上 自顶向下的算法用递归的方法,先解决左边的排序,再解决右边的排序 自底向上的算法用拆解合并的思想,先拆成size/2个小数组进行归并排序,继而将结果拆成size/4个数组归并排序,当size/(2**n)<1时完成排序 时间复杂度:平均O(nlog2n),最坏O(nlog2n),最好O(nlog2n) 空间复杂度:O(n)(需要一个临时数组来保存) 稳定性:稳定 """ class merge(object): #原地归并抽象方法,方便复用,传入数组,左值,中值,右值 def merge_sort(self, sort_list, lo, mid, hi): i = lo j = mid+1 aux = copy.deepcopy(sort_list) for k in range(lo, hi+1): if i > mid: sort_list[k] = aux[j] j += 1 elif j > hi: sort_list[k] = aux[i] i += 1 elif aux[j] <= aux[i]: sort_list[k] = aux[j] j += 1 else: sort_list[k] = aux[i] i += 1 def sort(self, sort_list): self.sort1(sort_list, 0, size-1) #自顶向下的归并排序 def sort1(self, sort_list, lo, hi): if hi <= lo: return sort_list mid = lo + (hi-lo)//2 self.sort1(sort_list, lo, mid) self.sort1(sort_list, mid+1, hi) self.merge_sort(sort_list, lo, mid, hi) def sort2(self, sort_list): sz = 1 while sz < size: lo = 0 while lo < size-sz: self.merge_sort(sort_list, lo, lo+sz-1, min(lo+sz+sz-1, size-1)) lo += sz+sz sz = sz+sz print(sort_list) 快速排序 """ 快速排序,是常规条件下最快的排序算法,使用分治算法思想,利用递归完成 首先先改变数组内部顺序(消除输入依赖),然后通过切分函数找出一个值(二分切分中,该值越接近正确顺序的中值越好) 以该值为mid,递归调用自身,分而治之 重点在于切分函数,二分切分函数的思想是,以某子数组第一个数a为基准, 从左往右扫描找出一个大于a的数,再从右往左扫描找出一个小于a的数,两者交换 最后将a放到正确的位置,返回切分的数的索引 时间复杂度:平均O(nlog2n),最坏O(n**2),最好O(nlog2n) 空间复杂度:O(log2n)(需要一个临时数组来保存) 稳定性:不稳定 """ class quick(object): #消除输入依赖 def sort(self, sort_list): random.sample(sort_list, size) self.sort1(sort_list, 0, size-1) #递归主函数体,从切分函数得到切分索引,左右递归,递归结束不用归并 def sort1(self, sort_list, lo, hi): if hi <= lo: return sort_list j = self.partition(sort_list, lo, hi) self.sort1(sort_list, lo, j-1) self.sort1(sort_list, j+1, hi) #切分函数,左右指针,轮流扫描,交换位置,最后将切分元素放到正确的位置,返回切分索引 def partition(self, sort_list, lo, hi): i = lo j = hi+1 v = sort_list[lo] while True: i = i + 1 while sort_list[i]<v: if i==hi: break i += 1 j = j - 1 while v < sort_list[j]: if j==lo: break j -= 1 if i >= j: break sort_list[i], sort_list[j] = sort_list[j], sort_list[i] sort_list[lo], sort_list[j] = sort_list[j], sort_list[lo] return j 基数排序 """ 基数排序,不进行比较的整数排序算法,基数指的是整数的进制(默认为10), 根据位数要做几次不同的桶排序,位数的计算为int(math.ceil(math.log(max(sort_list)+1, radix))) 每次循环完成当前位数(个位、十位、百位)的大小排序,理解过程可见http://bubkoo.com/2014/01/15/sort-algorithm/radix-sort/ 一共有十个桶,分别对应0-10,每个桶有若干数据,则桶可以用二维数组完成,记为a[index1][index2], 对每一个sort_list里的数,index1 = sort_list_num%(radix**i)//(radix**(i-1)) 时间复杂度:平均O(k*n),最坏O(k*n),最好O(k*n),k为最大数字的位数 空间复杂度:O(n) 稳定性:稳定 """ def radix_sort(sort_list, radix=10): """sort_list为整数列表, radix为基数""" K = int(math.ceil(math.log(max(sort_list)+1, radix))) for i in range(1, K+1): bucket = [[] for i in range(radix)] for val in sort_list: bucket[val%(radix**i)//(radix**(i-1))].append(val) del sort_list[:] for each in bucket: sort_list.extend(each) print(sort_list) 测试 import copy import random import math sort_list = [20,1,24,54,11,26,87,45,32,544,25,87,47,48,58,1024] global size size = len(sort_list) #select_sort(sort_list) #bub_sort(sort_list) #insert_sort(sort_list) #shell_sort(sort_list) #自顶向下归并排序测试 # a = merge() # a.sort(sort_list) # print(sort_list) #自底向上归并排序测试 # a = merge() # a.sort2(sort_list) # print(sort_list) #快速排序测试 # print(sort_list) # a = quick() # a.sort(sort_list) # print(sort_list) #基数排序测试 #radix_sort(sort_list)

新冠肺炎记忆

杂谈 新冠肺炎记忆 这次新冠肺炎的出现让我想起了 03 年,那年才刚上小学,没有什么记忆,只是学还是要上,经常检测体温,家里买了很多白醋等记忆。 时间来到 2020 年,武汉爆发了新型冠状病毒肺炎,我大概 1 月 16 号得知这个情况,当时也没在意,20 号从杭州回到了老家,这一路没什么感受,一切都和往常一样。时间来到 23 号,武汉宣布封城,媒体们报道,希望春节不要到处串门,这个时候才意识到问题的严重性,但也只是觉得过完春节就没事了。 后面每个城市都出现了患者,确证人数逐日递增 各地医疗援助支援湖北,支援武汉 又出现了每个小区控制进出的策略 湖北省以及武汉市的主要领导被替换 全国各地甚至全球范围口罩都抢不到 多省物流停滞,导致商品堆积 多趟列车以及飞机停止运营 武汉红十字会受到质疑 各种店面关闭,唯独超市还在运营,但人数稀稀拉拉,东西也越来越少 随着时间的推进,除了这些坏消息,也出现了很多的好消息。 全球支援医疗物资 国内工厂拼命的生产医疗物资 各地蔬菜以及肉类支援湖北 一省援助一市策略 调派军医支援湖北 火神山、雷神山、方舱医院 人人自危的关头,仍旧有人站在前线,这就像是一场战争。作为一个个体,不出门添乱也是一种贡献,但是二月中旬该回去上班了,保护好自己也是非常重要的,下面的是关于返程的一些记忆。 从老家出发,自助取票大厅空无一人,进入候车室,人数大约只有平时的 1/30,每个人坐在座位上都和旁边的人隔着 10 个以上的座位。 坐的是 k 车,上车时没有排队,直接进去,找到座位后只有一个人,大概从售票的时候就已经控制票数。k 车的结构是一排 5 个人,中间有个过道划分为 3 个座位和 2 个座位,现在每个人独占 3 个座位或者 2 个座位。火车上要求去杭州的乘客把身份证准备好,但最后也没人来查。火车座位上靠近头部的地方有个白色的布垫子,中间的时候有个乘务员戴着手套全部取走了(可能是消毒或者一次性的)。全程没有各种火车上的叫卖,什么小玩具,修剪工具,内蒙古的奶糖之类的,唯独有盒饭在卖,也只有 10 块钱一份的种类。快下车的时候,乘务员来收拾桌上的垃圾,带着手套,不仅收走了垃圾,连盛放垃圾的盘子也一并回收。 下车后,来自重点疫区的走单独的通道,需要填表格以及量体温;来自一般地区的人则需要用身份证录入到一个系统中(可能是验证杭州健康码?)。火车出站口没有了昔日各种旅馆旅行社的身影。地铁一次体温检测,下车后暴雨如注,叫了辆滴滴,坐在城市看着外面下着的雨。到小区门口了,根据要求填写各种表格,主要的就是《自愿隔离表》,填写后拿个温度计,体温记录表和代购券就可以进门进行自我隔离了。7 天的泡面生活开始。 整个旅程大概五个小时,我的观察只是这个大社会微小的一部分。完。

更新 node.js

[[Node.js]] How to Update Node.js to Latest Version (Linux, Ubuntu, OSX, Others) origin-link: How to Update Node.js to Latest Version (Linux, Ubuntu, OSX, Others) As with so many open-source technologies, Node.js is a fast-moving project. Minor updates come out every few weeks to boost stability and security among all version branches. Methods abound for updating Node on any operating system, so you don’t have an excuse to fall behind. We’ve compiled some of the simplest and most effective ways to install the newest version of Node on Linux-based, Windows, and macOS machines. Before you get started, check which version of Node.js you’re currently using by running node -v in a command line terminal. ...

栈的 Python 实现(列表)

Python [[Data Structure]] 栈的 Python 实现(列表) class Stack(object): """栈的 Python 列表实现 """ # 初始化对象,生成空列表 def __init__(self): self.item = [] # 判断栈是否为空,返回 True or False def isEmpty(self): return self.item == [] # 入栈方法,添加至列表尾部 def push(self, item): self.item.append(item) # 出栈方法,从队列尾部弹出,并返回弹出值 def pop(self): return self.item.pop() # 查栈顶元素方法,返回栈顶元素值,但不弹出 def peek(self): return self.item[len(self.item)-1] # 查看栈的大小方法,返回 int 值 def size(self): return len(self.item) def match_sympol(string): """ 利用栈的特性(后进先出),对三种常见括号进行匹配 对于左括号,直接压入栈,遇到右括号,弹出栈顶元素,若能一一对应,且最终栈为空,则完全匹配 """ s = Stack() flag = True index = 0 while index < len(string) and flag: symbol = string[index] if symbol in "({[": s.push(symbol) else: if s.isEmpty(): flag = False else: top = s.pop() if not matches_help(top, symbol): flag = False index += 1 if flag and s.isEmpty(): return True else: return False def matches_help(open, close): """ 匹配辅助函数 传入两个 char 参数,判断是否对应,返回 True or False """ opens = "({[" closes = ")}]" return opens.index(open) == closes.index(close) def ten2two(num): """ 十进制转二进制,原理是除二取余法 将十进制数除二取余数,将余数压入栈,直到十进制数为 0,然后栈逐个弹出 """ s = Stack() while num > 0: rem = num%2 s.push(rem) num = num//2 binString = "" while not s.isEmpty(): binString = binString + str(s.pop()) return binString def infixToPostfix(infixexpr): """ 中序表达式转后续表达式 中序表达式转换后续表达式有个简单的方法,先将中序表达式的每一次运算都加上括号,接着从右往左, 找到第一个算数符号替换掉最近的右括号,并将对应的左括号去除,继续往左执行,直到没有括号为止 具体过程: 1、创建一个名为 opstack 的空栈以保存运算符。给输出创建一个空列表。 2、通过使用字符串方法拆分将输入的中缀字符串转换为标记列表。 3、从左到右扫描标记列表。 如果标记是操作数,将其附加到输出列表的末尾。 如果标记是左括号,将其压到 opstack 上。 如果标记是右括号,则弹出 opstack,直到删除相应的左括号。将每个运算符附加到输出列表的末尾。 如果标记是运算符,*,/,+或 - ,将其压入 opstack。但是,首先删除已经在 opstack 中具有更高或相等优先级的任何运算符,并将它们加到输出列表中。 4、当输入表达式被完全处理时,检查 opstack。仍然在栈上的任何运算符都可以删除并加到输出列表的末尾。 理解过程可见:https://facert.gitbooks.io/python-data-structure-cn/3.%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/3.9.%E4%B8%AD%E7%BC%80%E5%89%8D%E7%BC%80%E5%92%8C%E5%90%8E%E7%BC%80%E8%A1%A8%E8%BE%BE%E5%BC%8F/ """ prec = {} prec["*"] = 3 prec["/"] = 3 prec["+"] = 2 prec["-"] = 2 prec["("] = 1 opStack = Stack() postfixList = [] tokenList = infixexpr.split() for token in tokenList: if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789": postfixList.append(token) elif token == '(': opStack.push(token) elif token == ')': topToken = opStack.pop() while topToken != '(': postfixList.append(topToken) topToken = opStack.pop() else: while (not opStack.isEmpty()) and (prec[opStack.peek()] >= prec[token]): postfixList.append(opStack.pop()) opStack.push(token) while not opStack.isEmpty(): postfixList.append(opStack.pop()) return " ".join(postfixList) def postfixEval(postfixExpr): """ 后缀表达式的算值 根据后缀表达式的特点,很容易可以想到,将运算数压入栈,当出现符号时,弹出栈顶两个元素,计算完成后压入栈, 等待下一个运算数或者运算符,最后栈顶元素就是后缀表达式的值。 """ operandStack = Stack() tokenList = postfixExpr.split() for token in tokenList: if token in "0123456789": operandStack.push(int(token)) else: operand2 = operandStack.pop() operand1 = operandStack.pop() result = doMath(token, operand1, operand2) operandStack.push(result) return operandStack.pop() def doMath(op, op1, op2): """ 后缀表达式运算值的辅助函数 输入三个参数(运算符,操作数1,操作数2),返回运算结果。 """ if op == "*": return op1 * op2 elif op == "/": return op1 / op2 elif op == "+": return op1 + op2 else: return op1 - op2 #栈的测试 # s= Stack() # print(s.isEmpty()) # s.push(4) # s.push('dog') # print(s.peek()) # s.push(True) # print(s.size()) # print(s.isEmpty()) # s.push(8.4) # print(s.pop()) # print(s.pop()) # print(s.size()) # 符号匹配的测试 # print(match_sympol('')) # 十进制数转二进制的测试 # print(ten2two(100)) # 中缀表达式转后缀表达式的测试 # print(infixToPostfix("( A + B ) * C - ( D - E ) * ( F + G )")) # 后缀表达式的求求值 # print(postfixEval('7 8 + 3 2 + /'))

正则表达式及re库的使用

Python [[Regular Expression]] 正则表达式及re库的使用 一、正则表达式 正则表达式要做的事简单地说就是字符串匹配,它由普通字符、非打印字符、特殊字符、限定符和定位符构成。理论上可以匹配一切字符串。 二、什么是正则表达式 正则表达式30分钟入门教程 正则表达式手册 三、在线正则表达式测试 regex101 在线正则表达式测试 三、常用的匹配规则 模式 描述 | 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。串行“\”匹配“\”而“(”则匹配“(”。 ^ 匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。 $ 匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。 * 匹配前面的子表达式零次或多次。例如,zo*能匹配“z”以及“zoo”。*等价于{0,}。 + 匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。 ? 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“does”或“does”中的“do”。?等价于{0,1}。 {n} n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。 {n,} n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。 {n,m} m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。 ? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。 . 匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“(. (pattern) 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\(”或“\)”。 (?:pattern) 匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。 (?=pattern) 正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 (?!pattern) 正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始 (?<=pattern) 反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。 (?<!pattern) 反向否定预查,与正向否定预查类拟,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。 x|y 匹配x或y。例如,“z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。 [xyz] 字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。 [^xyz] 负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“p”。 [a-z] 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。 [^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。 \b 匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。 \B 匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。 \cx 匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。 \d 匹配一个数字字符。等价于[0-9]。 \D 匹配一个非数字字符。等价于[^0-9]。 \f 匹配一个换页符。等价于\x0c和\cL。 \n 匹配一个换行符。等价于\x0a和\cJ。 \r 匹配一个回车符。等价于\x0d和\cM。 \s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。 \S 匹配任何非空白字符。等价于[^ \f\n\r\t\v]。 \t 匹配一个制表符。等价于\x09和\cI。 \v 匹配一个垂直制表符。等价于\x0b和\cK。 \w 匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。 \W 匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。 四、python 里的 re模块 4.1 match 这里首先介绍第一个常用的匹配方法 —— match,向它传入要匹配的字符串以及正则表达式,就可以检测这个正则表达式是否匹配字符串。 ...

硬 raid 快速配置

Linux RAID 硬 raid 快速配置 查看并清理 foreign configure /opt/MegaRAID/storcli/storcli64 /c0/fall show /opt/MegaRAID/storcli/storcli64 /c0/fall del 查看物理磁盘,确定数目和 eid 和 sid,一般是连续的 /opt/MegaRAID/storcli/storcli64 /c0/eall/sall show 配置 raid 一般需要确认 enclosure ID 和 Slot ID。 在这里 eid 就是 8,sid 就是 0-9 一共九块盘。根据第二步看到的数据进行修改。 /opt/MegaRAID/storcli/storcli64 /c0 add vd r5 size=all name=lotus drives=8:0-9 AWB ra direct strip=256 上述是一般 raid 创建,我们的存储机器有 36 块盘,所以指定前 18 块盘是一个 raid5,接着 17 块盘是一个 raid5,最后一个盘是全局热备盘。以 172.16.10.11 为例子: sudo /opt/MegaRAID/storcli/storcli64 /c0 add vd r5 size=all name=lotus drives=25:1-18 AWB ra direct strip=1024 sudo /opt/MegaRAID/storcli/storcli64 /c0 add vd r5 size=all name=lotus-2 drives=20:1-23 AWB ra direct strip=1024 sudo /opt/MegaRAID/storcli/storcli64 /c0/e26/s12 add hotsparedrive 根据实际需求,确定 eid 和 slot id,生成 raid,最后一步指定一个全局热备盘。 ...

硬盘接口-规范协议-传输总线

Hardware 硬盘接口 - 规范协议 - 传输总线 硬盘接口说明 硬盘对计算机总的来说,算是“外设”,具备容易扩展的特性,也拥有多种不同的接口。 IDE 接口 IDE 的英文全称为 “Integrated Drive Electronics”,即“电子集成驱动器”,它的本意是指把“硬盘控制器”与“盘体”集成在一起的硬盘驱动器。把盘体与控制器集成在一起的做法减少了硬盘接口的电缆数目与长度,数据传输的可靠性得到了增强。 不过 IDE 不仅指接口形式,主要还指硬盘的形式,即 IDE 硬盘,但人们习惯用 IDE 来统称 PATA 接口类的硬盘。 而 PATA 接口单纯指硬盘的接口形式,即“并行接口”。与之对应的是 SATA,即“串行接口”。其实 PATA 接口(并行接口)与 SATA(串行接口)的硬盘的严格上说都是IDE硬盘。如果说“PATA 接口的 IDE 硬盘"(简称 IDE 硬盘)和“SATA 接口的 IDE 硬盘”(简称 SATA 硬盘)会更准确点。 SATA 接口 使用 SATA(Serial ATA)口的硬盘又叫串口硬盘。Serial ATA 采用串行连接方式,串行 ATA 总线使用嵌入式时钟信号,具备了更强的纠错能力,与以往相比其最大的区别在于能对传输指令(不仅仅是数据)进行检查,如果发现错误会自动矫正,这在很大程度上提高了数据传输的可靠性。串行接口还具有结构简单、支持热插拔的优点。串口硬盘是一种完全不同于 PATA 的新型硬盘接口类型,由于采用串行方式传输数据而知名。相对于 PATA 来说,就具有非常多的优势。首先,Serial ATA 以连续串行的方式传送数据,一次只会传送 1 位数据。这样能减少 SATA 接口的针脚数目,使连接电缆数目变少,效率也会更高。(但 SATA 本质上是 SATA 接口的 IDE 硬盘。) SATA 3.0 能达到带宽 6Gb/s,速度 600MB/s。 ...

蓝鲸智云 5.1.29 搭建

Monitoring 蓝鲸智云 5.1.29 搭建 由于官方的安装指导文件更新于 2020-01-20,而 5.1.29 的安装包则是 2020-09-27 释出的,所以安装过程中会有坑,这里会对整个安装过程进行记录。 获取安装包并检验MD5码 axel https://bkopen-1252002024.file.myqcloud.com/ce/bkce_src-6.0.0.tgz -n 10 --output=/root/bkce_src-6.0.0.tgz md5sum /root/bkce_src-6.0.0.tgz 操作系统配置 1. yum 源更新 # centos7 为基准 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.cloud.tencent.com/repo/centos7_base.repo yum clean all yum makecache # centos7 epel mv /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.backup wget -O /etc/yum.repos.d/epel.repo http://mirrors.cloud.tencent.com/repo/epel-7.repo yum clean all yum makecache 2. 关闭 SELinux # 检查 SELinux 的状态,如果它已经禁用,可以跳过后面的命令 sestatus # 通过命令临时禁用 SELinux setenforce 0 # 或者修改配置文件 sed -i 's/^SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config reboot 3. 关闭默认防火墙(firewalld) # 检查默认防火墙状态,如果返回 not running,可以跳过后面的命令 firewall-cmd --state systemctl stop firewalld # 停止 firewalld systemctl disable firewalld # 禁用 firewall 开机启动 4. 安装 rsync 命令 # 检查是否有 rsync 命令,如果有返回 rsync 路径,可以跳过后面的命令 which rsync # 安装 rsync yum -y install rsync 5. 停止并禁用 NetWorkManager # 检查 NetworkManager 运行状态 systemctl status NetworkManager # 关闭并禁用 NetworkManager systemctl stop NetworkManager systemctl disable NetworkManager 6. 调整最大文件打开数 # 检查当前 root 账号下的 max open files 值 ulimit -n # 备份之前的配置文件 cp /etc/security/limits.conf /etc/security/limits.conf.bak # 增加配置信息 cat << EOF >> /etc/security/limits.conf root soft nofile 102400 root hard nofile 102400 EOF 7. 确认服务器时间同步 # 检查每台机器当前时间和时区是否一致,若相互之间差别大于3s(考虑批量执行时的时差),建议校时。 date -R # 查看和ntp server的时间差异(需要外网访问,如果内网有ntpd服务器,自行替换域名为该服务的地址) ntpdate -d cn.pool.ntp.org 如果输出的最后一行 offset 大于 1s 建议校时。 # 和 ntp 服务器同步时间 ntpdate cn.pool.ntp.org # 使用 ntpd 进行时间同步 http://xstarcd.github.io/wiki/sysadmin/ntpd.html 8. 检查 resolv.conf 是否有修改权限 检查 /etc/resolv.conf 是否被加密无法修改(即便是 root),执行如下命令,检查是否有“i”加密字样: ...