为什么使用正则test( )第一次是 true,第二次是false?

本文解释了JavaScript中正则表达式的lastIndex属性如何影响连续匹配的结果,并提供了两种解决匹配失败问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.前言


今天朋友问我一个问题,我现在需要多次匹配同一个内容,但是为什么我第一次匹配,直接是 true,而第二次匹配确实 false 呢?

var s1 = "MRLP";
var s2 = "MRLP";
var reg = /mrlp/ig;
console.log(reg.test(s1));
console.log(reg.test(s2));

这时候你会发现,我们在连续使用一个正则匹配其他字符串的时候,第一次匹配是 true,而第二次匹配则是 false。

等等,WHT?我匹配的是 MRLP,而且我还特意加上i 用于不区分大小写,可以为什么第一次可以正常匹配,第二次就不行了呢?

这也就是我今天要跟大家说的,关于 JS 中的 lastIndex。

2. lastIndex

在开始讲解之前,首先先带大家简单回顾一下 JS中正则表达式的使用方式

JS 中正则表达式的使用方式有两种:

第一种是正则表达式对象的方法,常用方法有两个。

  • exec(str) : 检索字符串中指定的值。返回找到的值,并确定其位置
  • test(str) : 检索字符串中指定的值。返回 true 或 false

第二种是字符串对象的方法,常用方法有四个。

  • match(regexp) : 找到一个或多个正则表达式的匹配
  • replace(regexp) : 替换与正则表达式匹配的子串
  • search(regexp) : 检索与正则表达式相匹配的值
  • split(search) : 把字符串分割为字符串数组

而这些方法和咱们今天所说的 lastIndex 有什么关系呢?

lastIndex 属性用于规定下次匹配的起始位置

上次匹配的结果是由方法 RegExp.exec( )RegExp.test( ) 找到的,它们都以 lastIndex 属性所指的位置作为下次检索的起始点。

这样,就可以通过反复调用这两个方法来遍历一个字符串中的所有匹配文本。

而且需要注意,该属性只有设置标志 g才能使用

既然已经知道这个东西的形成原因,那么解决起来就非常简单了。

3.解决方案

3.1 第一种解决方案

如上面所述,我们 lastIndex 属性必须要设置 g 标签才能使用。

那么我们在匹配的时候,可以根据情况,直接去掉 g 标签就可以啦。

var s1 = "MRLP";
var s2 = "MRLP";
var reg = /mrlp/i;
console.log(reg.test(s1));  //true
console.log(reg.test(s2));  //true

3.2 第二种解决方案

很多时候,我们必须要执行 全局匹配( g ),这时候就不能使用第一种方案了。

其实,我们的lastIndex 属性是可读可写的

只要目标字符串的下一次搜索开始,就可以对它进行设置。

当方法 exec() 或 test() 再也找不到可以匹配的文本时,它们会自动把 lastIndex 属性重置为 0。

这样,我们再次执行全局匹配的时候,就不会出现 false 的情况了。

var s1 = "3206064928:MRLP:3206064928";
var s2 = "MRLP";
var reg = /mrlp/ig;
console.log(reg.test(s1));  //true
console.log(reg.lastIndex);   //reg.lastIndex = 15
reg.lastIndex = 0;          //这里我将 lastIndex 重置为 0
console.log(reg.test(s2));  //true
<think>我们使用Python的re模块进行正则表达式匹配。以下是一个详细的指南,包含常见操作和代码示例。 正则表达式是一种强大的字符串匹配和处理工具。Python通过re模块提供对正则表达式的支持。 主要步骤: 1. 导入re模块 2. 编写正则表达式模式 3. 选择re模块的函数(如match、search、findall等)进行匹配 4. 处理匹配结果 常用函数: - re.match(pattern, string): 从字符串起始位置匹配,如果起始位置匹配不成功,则返回None。 - re.search(pattern, string): 扫描整个字符串并返回第一个成功的匹配。 - re.findall(pattern, string): 返回字符串中所有匹配的子串,以列表形式返回。 - re.finditer(pattern, string): 返回一个迭代器,包含所有匹配对象。 - re.sub(pattern, repl, string): 替换字符串中匹配的子串。 匹配对象(Match object)的方法: - group(): 返回匹配的整个字符串 - groups(): 返回一个包含所有小组字符串的元组 - start(): 返回匹配开始的位置 - end(): 返回匹配结束的位置 - span(): 返回一个元组包含匹配(开始,结束)的位置 编译正则表达式(提高效率): 当同一个正则表达式被多次使用时,可以将其编译成正则表达式对象,以提高效率。 pattern = re.compile(r'pattern') 然后使用 pattern.match(), pattern.search()方法。 特殊字符: . 匹配任意字符(除了换行符) ^ 匹配字符串开头 $ 匹配字符串结尾 * 匹配0次或多次 + 匹配1次或多次 ? 匹配0次或1次 {m} 匹配m次 {m,n} 匹配m到n次 [] 字符集,如[abc]匹配a、b、c | 或,如A|B匹配A或B () 分组 \d 匹配数字,等价于[0-9] \D 匹配非数字 \s 匹配空白字符(空格、制表符等) \S 匹配非空白字符 \w 匹配字母数字及下划线,等价于[a-zA-Z0-9_] \W 匹配非字母数字及下划线 注意:在Python中,使用原始字符串(r'string')可以避免转义问题。 下面是一个完整的示例代码,展示各种常见用法:</think># Python 正则表达式字符串匹配详解 在 Python 中使用正则表达式进行字符串匹配主要通过 `re` 模块实现。下面我将详细介绍正则表达式的使用方法和常见应用场景。 ## 基础正则表达式匹配 ### 1. 导入 re 模块 ```python import re ``` ### 2. 基本匹配方法 ```python # 示例字符串 text = "Python is a powerful programming language. Python is also versatile." # 1. re.match() - 从字符串开头匹配 match_result = re.match(r'Python', text) print(match_result) # <re.Match object; span=(0, 6), match='Python'> # 2. re.search() - 搜索整个字符串 search_result = re.search(r'powerful', text) print(search_result) # <re.Match object; span=(10, 17), match='powerful'> # 3. re.findall() - 查找所有匹配项 all_matches = re.findall(r'Python', text) print(all_matches) # ['Python', 'Python'] # 4. re.finditer() - 返回匹配对象迭代器 for match in re.finditer(r'Python', text): print(f"Found '{match.group()}' at position {match.start()}-{match.end()}") # Found 'Python' at position 0-6 # Found 'Python' at position 40-46 # 5. re.fullmatch() - 整个字符串匹配 full_match = re.fullmatch(r'.*', text) print(full_match) # <re.Match object; span=(0, 55), match='Python is a powerful programming language. Python is also > ``` ## 常用正则表达式模式 ### 基本模式示例 ```python # 匹配数字 numbers = re.findall(r'\d+', "Phone: 123-4567, Cell: 987-6543") print(numbers) # ['123', '4567', '987', '6543'] # 匹配单词 words = re.findall(r'\w+', "Hello, world! 2023") print(words) # ['Hello', 'world', '2023'] # 匹配邮箱地址 emails = re.findall(r'[\w\.-]+@[\w\.-]+', "Contact: user@example.com, support@domain.org") print(emails) # ['user@example.com', 'support@domain.org'] # 匹配URL urls = re.findall(r'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', "Visit https://example.com or http://sub.domain.org") print(urls) # ['https://example.com', 'http://sub.domain.org'] ``` ## 高级匹配技巧 ### 1. 分组和捕获 ```python # 分组提取 date_text = "2023-10-25, 2024-01-15" matches = re.findall(r'(\d{4})-(\d{2})-(\d{2})', date_text) print(matches) # [('2023', '10', '25'), ('2024', '01', '15')] # 命名分组 pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})' match = re.search(pattern, "Event date: 2023-10-25") if match: print(f"Year: {match.group('year')}, Month: {match.group('month')}, Day: {match.group('day')}") # Year: 2023, Month: 10, Day: 25 ``` ### 2. 非捕获组和超前/后行断言 ```python # 非捕获组 (?:...) text = "color: red; color: blue; color: green" colors = re.findall(r'color: (?:red|blue|green)', text) print(colors) # ['color: red', 'color: blue', 'color: green'] # 正向前瞻 (?=...) # 匹配后面跟着"元"的"Python" matches = re.findall(r'Python(?=元)', "Python编程 Python元编程") print(matches) # ['Python'] (只匹配第二个Python) # 负向前瞻 (?!...) # 匹配后面不是"元"的"Python" matches = re.findall(r'Python(?!元)', "Python编程 Python元编程") print(matches) # ['Python'] (只匹配第一个Python) # 正向后顾 (?<=...) # 匹配前面是"元"的"Python" matches = re.findall(r'(?<=元)Python', "元Python编程") print(matches) # ['Python'] # 负向后顾 (?<!...) # 匹配前面不是"元"的"Python" matches = re.findall(r'(?<!元)Python', "元Python 普通Python") print(matches) # ['Python'] (只匹配第二个Python) ``` ### 3. 编译正则表达式 ```python # 编译正则表达式对象(提高性能) pattern = re.compile(r'\b[A-Z][a-z]+\b') # 匹配首字母大写的单词 text = "John Smith and Alice Johnson work at Google." matches = pattern.findall(text) print(matches) # ['John', 'Smith', 'Alice', 'Johnson', 'Google'] ``` ## 字符串操作与正则表达式 ### 1. 字符串替换 ```python # 简单替换 text = "Python 2.7 and Python 3.10" new_text = re.sub(r'Python 2\.7', 'Python 3.x', text) print(new_text) # "Python 3.x and Python 3.10" # 使用函数进行替换 def increment_version(match): version = int(match.group(1)) return f"Python {version + 1}" text = "Python 2, Python 3, Python 4" new_text = re.sub(r'Python (\d)', increment_version, text) print(new_text) # "Python 3, Python 4, Python 5" ``` ### 2. 字符串分割 ```python text = "apple,banana;cherry|date" parts = re.split(r'[,;|]', text) print(parts) # ['apple', 'banana', 'cherry', 'date'] # 保留分隔符 parts = re.split(r'([,;|])', text) print(parts) # ['apple', ',', 'banana', ';', 'cherry', '|', 'date'] ``` ## 实际应用案例 ### 1. 验证输入格式 ```python def validate_email(email): """验证电子邮件地址格式""" pattern = r'^[\w\.-]+@[\w\.-]+\.\w{2,}$' return bool(re.match(pattern, email)) print(validate_email("user@example.com")) # True print(validate_email("invalid.email@")) # False print(validate_email("name@domain.co.uk")) # True ``` ### 2. 提取日志信息 ```python log = """ [2023-10-25 08:30:45] INFO: User 'admin' logged in [2023-10-25 09:15:22] ERROR: Database connection failed [2023-10-25 10:05:33] WARNING: Disk space low """ pattern = r'\[(.*?)\] (\w+): (.*)' for match in re.finditer(pattern, log): timestamp, level, message = match.groups() print(f"{timestamp} | {level:7} | {message}") # 输出: # 2023-10-25 08:30:45 | INFO | User 'admin' logged in # 2023-10-25 09:15:22 | ERROR | Database connection failed # 2023-10-25 10:05:33 | WARNING | Disk space low ``` ### 3. 解析复杂文本 ```python html = """ <div class="product"> <h3>Product A</h3> <p>Price: $19.99</p> <p>Rating: 4.5/5</p> </div> <div class="product"> <h3>Product B</h3> <p>Price: $29.99</p> <p>Rating: 3.8/5</p> </div> """ pattern = r'<div class="product">.*?<h3>(.*?)</h3>.*?<p>Price: \$(.*?)</p>.*?<p>Rating: (.*?)/5</p>' products = re.findall(pattern, html, re.DOTALL) for name, price, rating in products: print(f"Product: {name}, Price: ${price}, Rating: {rating}/5") # 输出: # Product: Product A, Price: $19.99, Rating: 4.5/5 # Product: Product B, Price: $29.99, Rating: 3.8/5 ``` ## 性能优化与最佳实践 1. **预编译正则表达式**:对于重复使用的模式,使用 `re.compile()` 可以提高性能 2. **使用原始字符串**:在模式字符串前加 `r` 前缀(如 `r'\d+'`)避免转义问题 3. **避免贪婪匹配**:在量词后加 `?` 使其变为非贪婪匹配(如 `.*?`) 4. **使用合适的函数**: - 只需检查是否存在匹配:`re.search()` - 需要提取所有匹配:`re.findall()` 或 `re.finditer()` - 需要替换内容:`re.sub()` 5. **考虑使用第三方库**:对于复杂需求,可使用 `regex` 模块(支持更多特性) ```python # 性能比较示例 import timeit # 未编译 def test_uncompiled(): return bool(re.search(r'\d{3}-\d{3}-\d{4}', "Phone: 123-456-7890")) # 预编译 compiled_pattern = re.compile(r'\d{3}-\d{3}-\d{4}') def test_compiled(): return bool(compiled_pattern.search("Phone: 123-456-7890")) # 测试性能 print("未编译:", timeit.timeit(test_uncompiled, number=100000)) print("预编译:", timeit.timeit(test_compiled, number=100000)) # 输出示例(可能): # 未编译: 0.15 seconds # 预编译: 0.08 seconds ``` ## 正则表达式调试工具 1. **在线正则表达式测试器**: - [regex101.com](https://regex101.com/) - [regexr.com](https://regexr.com/) 2. **Python内置调试**: ```python pattern = re.compile(r'(\d{3})-(\d{3})-(\d{4})') print(pattern.pattern) # 输出原始模式字符串 print(pattern.groups) # 输出分组数量 print(pattern.groupindex) # 输出命名分组映射 ``` 3. **使用 re.DEBUG 标志**: ```python re.compile(r'(\d{3})-(\d{3})-(\d{4})', re.DEBUG) # 输出: # SUBPATTERN 1 0 0 # MAX_REPEAT 3 3 # IN # CATEGORY CATEGORY_DIGIT # LITERAL 45 # SUBPATTERN 2 0 0 # MAX_REPEAT 3 3 # IN # CATEGORY CATEGORY_DIGIT # LITERAL 45 # SUBPATTERN 3 0 0 # MAX_REPEAT 4 4 # IN # CATEGORY CATEGORY_DIGIT ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值