断言

转自 http://www.cnblogs.com/iyangyuan/archive/2013/05/30/3107390.html

'123456'.replace(/(\d)(?=((\d{3})+$))/g, '$1,')

//即每3个数字,替换成原数字加','如果遇到字符尾,则不替换.用到了分组断言


正则表达式中的断言,作为高级应用出现,倒不是因为它有多难,而是概念比较抽象,不容易理解而已,今天就让小菜通俗的讲解一下。

       如果不用断言,以往用过的那些表达式,仅仅能获取到有规律的字符串,而不能获取无规律的字符串。

       举个例子,比如html源码中有<title>xxx</title>标签,用以前的知识,我们只能确定源码中的<title>和</title>是固定不变的。因此,如果想获取页面标题(xxx),充其量只能写一个类似于这样的表达式:<title>.*</title>,而这样写匹配出来的是完整的<title>xxx</title>标签,并不是单纯的页面标题xxx。

       想解决以上问题,就要用到断言知识。

       在讲断言之前,读者应该先了解分组,这有助于理解断言。

       分组在正则中用()表示,根据小菜理解,分组的作用有两个:

 

       n  将某些规律看成是一组,然后进行组级别的重复,可以得到意想不到的效果。

       n  分组之后,可以通过后向引用简化表达式。

 

 

       先来看第一个作用,对于IP地址的匹配,简单的可以写为如下形式:

       \d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}

       但仔细观察,我们可以发现一定的规律,可以把.\d{1,3}看成一个整体,也就是把他们看成一组,再把这个组重复3次即可。表达式如下:

       \d{1,3}(.\d{1,3}){3}

       这样一看,就比较简洁了。

      

再来看第二个作用,就拿匹配<title>xxx</title>标签来说,简单的正则可以这样写:

       <title>.*</title>

       可以看出,上边表达式中有两个title,完全一样,其实可以通过分组简写。表达式如下:

       <(title)>.*</\1>

       这个例子实际上就是反向引用的实际应用。对于分组而言,整个表达式永远算作第0组,在本例中,第0组是<(title)>.*</\1>,然后从左到右,依次为分组编号,因此,(title)是第1组。

       用\1这种语法,可以引用某组的文本内容,\1当然就是引用第1组的文本内容了,这样一来,就可以简化正则表达式,只写一次title,把它放在组里,然后在后边引用即可。

       以此为启发,我们可不可以简化刚刚的IP地址正则表达式呢?原来的表达式为\d{1,3}(.\d{1,3}){3},里边的\d{1,3}重复了两次,如果利用后向引用简化,表达式如下:

       (\d{1,3})(.\1){3}

       简单的解释下,把\d{1,3}放在一组里,表示为(\d{1,3}),它是第1组,(.\1)是第2组,在第2组里通过\1语法,后向引用了第1组的文本内容

       经过实际测试,会发现这样写是错误的,为什么呢?

       小菜一直在强调,后向引用,引用的仅仅是文本内容,而不是正则表达式

       也就是说,组中的内容一旦匹配成功,后向引用,引用的就是匹配成功后的内容,引用的是结果,而不是表达式

       因此,(\d{1,3})(.\1){3}这个表达式实际上匹配的是四个数都相同的IP地址,比如:123.123.123.123。

      

       至此,读者已经掌握了传说中的后向引用,就这么简单。

 

       接下来说说什么是断言。

 

       所谓断言,就是指明某个字符串前边或者后边,将会出现满足某种规律的字符串。

       就拿文章开篇的例子来说,我们想要的是xxx,它没有规律,但是它前边肯定会有<title>,后边肯定会有</title>,这就足够了。

       想指定xxx前肯定会出现<title>,就用正后发断言,表达式:(?<=<title>).*

       向指定xxx后边肯定会出现</title>,就用正先行断言,表达式:.*(?=</title>)

       两个加在一起,就是(?<=<title>).*(?=</title>)

       这样就能匹配到xxx。

      

       相信读者看到这,已经蒙了,不用急,待小菜慢慢讲来。

      

       其实掌握了规律,就很简单了,无论是先行还是后发,都是相对于xxx而言的,也就是相对于目标字符串而言。

       假如目标字符串后边有条件,可以理解为目标字符串在前,就用先行断言,放在目标字符串之后。

       假如目标字符串前边有条件,可以理解为目标字符串在后,就用后发断言,放在目标字符串之前。

       假如指定满足某个条件,就是正。

       假如指定不满足某个条件,就是负。

       断言只是条件,帮你找到真正需要的字符串,本身并不会匹配!

      

(?=X )

零宽度正先行断言。仅当子表达式 X 在 此位置的右侧匹配时才继续匹配。例如,/w+(?=/d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。

(?!X)

零宽度负先行断言。仅当子表达式 X 不在 此位置的右侧匹配时才继续匹配。例如,例如,/w+(?!/d) 与后不跟数字的单词匹配,而不与该数字匹配 。

(?<=X)

零宽度正后发断言。仅当子表达式 X 在 此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。

(?<!X)

零宽度负后发断言。仅当子表达式 X 不在此位置的左侧匹配时才继续匹配。例如,(?<!19)99 与不跟在 19 后面的 99 的实例匹配

 

 

       从断言的表达形式可以看出,它用的就是分组符号,只不过开头都加了一个问号,这个问号就是在说这是一个非捕获组,这个组没有编号,不能用来后向引用,只能当做断言。

       教程到此结束,希望大家阅读愉快!


<think>我们正在讨论如何在编程中对数组进行断言。数组断言通常涉及验证数组满足某些条件,例如:数组不为空、数组长度在预期范围内、数组中的每个元素都满足特定属性等。根据提供的引用,我们可以参考静态断言(编译时)和动态断言(运行时)的实现方法,以及断言的应用场景(如前置条件、后置条件、类不变量等)。引用[2]提到了断言可以用于验证内部不变量、类不变量、控制流不变量、方法的前置条件和后置条件。对于数组,我们可以断言其长度或元素值。引用[3]给出了Java中使用断言检查数组元素是否非负的示例。引用[4]提到了动态断言和静态断言,并给出了静态断言的一种实现方式(使用数组大小不能为负数的规则)。针对数组的断言,我们可以分为两类:1.动态断言(运行时检查):在代码执行时检查数组的条件,如果不满足则触发断言失败。2.静态断言(编译时检查):主要用于在编译时检查数组相关的常量表达式(例如数组长度必须是常量表达式),不满足则编译失败。由于数组的长度通常在运行时才能确定(除非是编译时常量),所以大多数数组断言都是动态的。我们将提供几种常见编程语言中数组断言的示例:一、Java中的数组断言(动态)Java使用`assert`语句,需要在运行时开启断言(通过`-ea`参数)。示例1:检查数组不为空且长度大于0```javapublicvoidprocessArray(int[]arr){assertarr!=null:"数组不能为null";assertarr.length>0:"数组长度必须大于0";//其他处理}```示例2:检查数组中的每个元素都满足条件(如非负)如引用[3]中的例子:```javaimportjava.util.Scanner;publicclassTest{publicstaticvoidmain(String[]args){int[]score={-120,98,89,120,99};intsum=0;for(intnumber:score){assertnumber>=0:"负数不能是成绩";sum=sum+number;}System.out.println("总成绩:"+sum);}}```注意:运行Java程序时需加上`-ea`参数,否则断言不会生效。二、C/C++中的数组断言(动态)C/C++使用`assert`宏(需要包含`assert.h`或`cassert`),它在运行时检查条件,如果失败则终止程序并输出错误信息。示例:检查数组不为空(注意:在C/C++中,数组变量本质上是指针,但数组长度无法直接通过指针获得,所以通常需要传递长度)```c#include<assert.h>voidprocessArray(int*arr,intlength){assert(arr!=NULL);//检查指针不为NULLassert(length>0);//检查长度大于0//如果要检查数组的每个元素,可以使用循环for(inti=0;i< length;i++){assert(arr[i]>=0);//检查每个元素非负}}```三、静态断言(编译时检查)静态断言主要用于在编译时检查常量表达式。在C++11及以上,可以使用`static_assert`关键字。C语言中没有内置的静态断言,但可以通过宏模拟(如引用[1]和[4]所示)。示例(C++11):```cpp//检查某个编译时常量(如数组大小)是否符合要求constexprintarray_size=10;static_assert(array_size>0,"数组大小必须大于0");//或者用于类型检查等```注意:静态断言只能用于编译时常量表达式。四、使用测试框架进行数组断言(更常见于单元测试)在实际的单元测试中,我们通常使用测试框架提供的断言方法,这些断言更强大,且能提供更详细的错误信息。示例(Java中使用JUnit):```javaimportorg.junit.Test;importstaticorg.junit.Assert.*;publicclassArrayTest{@TestpublicvoidtestArray(){int[]arr={1,2,3};//断言数组不为空assertNotNull(arr);//断言数组长度assertEquals(3,arr.length);//断言数组内容assertArrayEquals(newint[]{1,2,3},arr);}}```示例(Python中使用pytest):```pythondeftest_array():arr=[1,2,3]assertarrisnotNoneassertlen(arr)==3assertarr==[1,2,3]```示例(JavaScript中使用Jest):```javascripttest('arraytest',()=>{constarr=[1,2,3];expect(arr).not.toBeNull();expect(arr).toHaveLength(3);expect(arr).toEqual([1,2,3]);});```综上所述,对数组进行断言的方法依赖于语言和场景。在开发阶段,可以使用语言的断言机制(如Java的assert,C/C++的assert)进行运行时检查;在编译时,可以使用静态断言检查常量条件;而在单元测试中,则使用测试框架的断言功能。根据用户要求,提供相关代码示例,我们已在上面给出多种语言的示例。注意:在发布版本中,动态断言通常被禁用,因此它们只用于调试阶段。在生产环境中,对于必须检查的条件,应使用错误处理机制(如异常)而非断言。§§相关问题§§1.静态断言和动态断言有什么区别?2.在单元测试中,如何对数组进行更复杂的断言(如部分匹配、顺序无关等)?3.为什么在生产环境中不推荐使用断言(如C/C++的assert)?有什么替代方案?</think> ### 数组断言的实现方法及代码示例 在编程中对数组进行断言主要有两种方式:**运行时动态断言**和**编译时静态断言**。以下是具体实现方法和多语言示例: --- #### 一、动态断言(运行时检查) 适用于验证数组状态或元素值,通过条件判断触发错误。 1. **Java 示例** - 检查成绩数组无负数: ```java public static void sumScores(int[] scores) { // 断言数组不为空 assert scores != null : "数组不能为null"; for (int score : scores) { // 断言每个元素≥0 assert score >= 0 : "成绩不能为负数:" + score; } } ``` *运行需添加JVM参数 `-ea`(开启断言)[^5]* 2. **C++ 示例** - 验证数组长度: ```cpp #include <cassert> void processArray(int* arr, size_t len) { // 断言指针非空且长度合法 assert(arr != nullptr && "数组指针不能为空"); assert(len > 0 && "数组长度必须大于0"); } ``` 3. **Python 示例** - 使用 `assert` 关键字: ```python def validate_coordinates(points): assert isinstance(points, list), "必须传入列表" assert all(len(p) == 2 for p in points), "每个元素必须是二维坐标" ``` --- #### 二、静态断言(编译时检查) 适用于验证编译时常量属性(如数组尺寸),错误在编译期抛出。 1. **C/C++ 静态断言**(C11/C++11 标准): ```cpp // 验证数组尺寸匹配 #define ARRAY_SIZE 10 static_assert(ARRAY_SIZE >= 5, "数组尺寸过小"); // 验证数据类型 static_assert(sizeof(int) == 4, "int必须为32位"); ``` 2. **自定义静态断言宏**(兼容旧标准): ```cpp #define STATIC_ASSERT(cond) typedef char static_check[(cond)?1:-1] STATIC_ASSERT(sizeof(float[3]) == 12); // 编译失败则报数组大小错误 ``` --- #### 三、进阶用法 1. **防御式编程规范**: ```java // Java类不变量检查示例 private boolean checkRep(int[] data) { // 验证数组状态 return data != null && data.length <= MAX_SIZE; } public void updateData() { // 方法执行前后检查 assert checkRep(this.data); // ...业务逻辑... assert checkRep(this.data); } ``` 2. **单元测试中断言**(JUnit示例): ```java @Test public void testArraySort() { int[] arr = {3, 1, 2}; Arrays.sort(arr); // 验证排序后数组 assertArrayEquals(new int[]{1, 2, 3}, arr); } ``` --- **关键原则**: 1. 动态断言用于运行时数据校验(如用户输入) 2. 静态断言用于编译时常量约束(如数组尺寸) 3. 类不变量验证在方法边界执行[^2] 4. 生产环境建议禁用断言(通过编译选项) > 示例完整代码需根据语言配置断言开关: > Java: `-ea`,C/C++: `-DNDEBUG`(关闭断言)[^5] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值