返回顶部
首页 > 资讯 > 精选 >JavaScript正则表达式中g标志实例分析
  • 693
分享到

JavaScript正则表达式中g标志实例分析

2023-06-29 13:06:24 693人浏览 泡泡鱼
摘要

本篇内容主要讲解“javascript正则表达式中g标志实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript正则表达式中g标志实例分析”吧!有一天在思否社区看到有个问题,大

本篇内容主要讲解“javascript正则表达式中g标志实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript正则表达式中g标志实例分析”吧!

    有一天在思否社区看到有个问题,大致描述如下

    const list = ['a', 'b', '-', 'c', 'd'];const reg = /[a-z]/g;const letters = list.filter(i => reg.test(i));// letters === ['a', 'c'];// 如果正则不使用`g`标志可以得到所有的字母// 为什么加入`g`之后就不可以了

    对问题而言,遍历中的i就是一个字符,不需要用到g。

    但是就我对正则的理解(过于浅薄)感觉上有没有g(只是全局搜索,不会匹配到就停下来)应该不影响,激发了我的好奇心。

    上面题的建议写法如下

    const reg = /[a-z]/g;reg.test('a'); // => truereg.test('a'); // => falsereg.test('a'); // => truereg.test('a'); // => falsereg.test('a'); // => true

    解密过程

    首先可以确定的表现一定是g导致的

    索引

    打开 MDN 仔细查看g标志的作用,得到结论和我的理解无二。

    我猜想应该就是g可能启用了某种缓存,又因为reg相对过滤器是全局变量,我将代码改为:

    const list = ['a', 'b', '-', 'c', 'd'];const letters = list.filter(i => /[a-z]/g.test(i));// letters === ['a', 'b', 'c', 'd'];

    将正则声明到每一次遍历,得到结论就是正确的,验证了我的猜想。也得到了,缓存就是正则中的某个地方

    下面我找到对应的源码来查看问题的原因

    源码层面

    由于最近在看 Rust,所以使用 Rust 编写的源码查看

    打开项目后,点击.进入 vscode 模式,command+p 搜索 regexp 关键词

    JavaScript正则表达式中g标志实例分析

    进入test.rs文件,command+f 搜索/g可以找到在 90 行有个last_index()测试

    #[test]fn last_index() {    let mut context = Context::default();    let init = r#"        var regex = /[0-9]+(\.[0-9]+)?/g;        "#;    // forward 的作用:更改 context,并返回结果的字符串。    eprintln!("{}", forward(&mut context, init));    assert_eq!(forward(&mut context, "regex.lastIndex"), "0");    assert_eq!(forward(&mut context, "regex.test('1.0foo')"), "true");    assert_eq!(forward(&mut context, "regex.lastIndex"), "3");    assert_eq!(forward(&mut context, "regex.test('1.0foo')"), "false");    assert_eq!(forward(&mut context, "regex.lastIndex"), "0");}

    看到了有lastIndex关键字,这里再已经大致猜到问题的原因了,g 标志存在匹配后的最后一个下标,导致出现问题。

    我们将视线移入到mod.rs文件中,搜索test

    在 631 行看到了fn test()方法

    pub(crate) fn test(    this: &jsValue,    args: &[JsValue],    context: &mut Context,) -> JsResult<JsValue> {    // 1. Let R be the this value.    // 2. If Type(R) is not Object, throw a TypeError exception.    let this = this.as_object().ok_or_else(|| {        context            .construct_type_error("RegExp.prototype.test method called on incompatible value")    })?;    // 3. Let string be ? ToString(S).    let arg_str = args        .get(0)        .cloned()        .unwrap_or_default()        .to_string(context)?;    // 4. Let match be ? RegExpExec(R, string).    let m = Self::abstract_exec(this, arg_str, context)?;    // 5. If match is not null, return true; else return false.    if m.is_some() {        Ok(JsValue::new(true))    } else {        Ok(JsValue::new(false))    }}

    test()方法中找到了Self::abstract_exec()方法

    pub(crate) fn abstract_exec(    this: &JsObject,    input: JsString,    context: &mut Context,) -> JsResult<Option<JsObject>> {    // 1. Assert: Type(R) is Object.    // 2. Assert: Type(S) is String.    // 3. Let exec be ? Get(R, "exec").    let exec = this.get("exec", context)?;    // 4. If IsCallable(exec) is true, then    if let Some(exec) = exec.as_callable() {        // a. Let result be ? Call(exec, R, « S »).        let result = exec.call(&this.clone().into(), &[input.into()], context)?;        // b. If Type(result) is neither Object nor Null, throw a TypeError exception.        if !result.is_object() && !result.is_null() {            return context.throw_type_error("regexp exec returned neither object nor null");        }        // c. Return result.        return Ok(result.as_object().cloned());    }    // 5. PerfORM ? RequireInternalSlot(R, [[RegExpMatcher]]).    if !this.is_regexp() {        return context.throw_type_error("RegExpExec called with invalid value");    }    // 6. Return ? RegExpBuiltinExec(R, S).    Self::abstract_builtin_exec(this, &input, context)}

    又在Self::abstract_exec()方法中找到了Self::abstract_builtin_exec()方法

    pub(crate) fn abstract_builtin_exec(    this: &JsObject,    input: &JsString,    context: &mut Context,) -> JsResult<Option<JsObject>> {    // 1. Assert: R is an initialized RegExp instance.    let rx = {        let obj = this.borrow();        if let Some(rx) = obj.as_regexp() {            rx.clone()        } else {            return context.throw_type_error("RegExpBuiltinExec called with invalid value");        }    };    // 2. Assert: Type(S) is String.    // 3. Let length be the number of code units in S.    let length = input.encode_utf16().count();    // 4. Let lastIndex be ℝ(? ToLength(? Get(R, "lastIndex"))).    let mut last_index = this.get("lastIndex", context)?.to_length(context)?;    // 5. Let flags be R.[[OriginalFlags]].    let flags = &rx.original_flags;    // 6. If flags contains "g", let global be true; else let global be false.    let global = flags.contains('g');    // 7. If flags contains "y", let sticky be true; else let sticky be false.    let sticky = flags.contains('y');    // 8. If global is false and sticky is false, set lastIndex to 0.    if !global && !sticky {        last_index = 0;    }    // 9. Let matcher be R.[[RegExpMatcher]].    let matcher = &rx.matcher;    // 10. If flags contains "u", let fullUnicode be true; else let fullUnicode be false.    let unicode = flags.contains('u');    // 11. Let matchSucceeded be false.    // 12. Repeat, while matchSucceeded is false,    let match_value = loop {        // a. If lastIndex > length, then        if last_index > length {            // i. If global is true or sticky is true, then            if global || sticky {                // 1. Perform ? Set(R, "lastIndex", +0????, true).                this.set("lastIndex", 0, true, context)?;            }            // ii. Return null.            return Ok(None);        }        // b. Let r be matcher(S, lastIndex).        // Check if last_index is a valid utf8 index into input.        let last_byte_index = match String::from_utf16(            &input.encode_utf16().take(last_index).collect::<Vec<u16>>(),        ) {            Ok(s) => s.len(),            Err(_) => {                return context                    .throw_type_error("Failed to get byte index from utf16 encoded string")            }        };        let r = matcher.find_from(input, last_byte_index).next();        match r {            // c. If r is failure, then            None => {                // i. If sticky is true, then                if sticky {                    // 1. Perform ? Set(R, "lastIndex", +0????, true).                    this.set("lastIndex", 0, true, context)?;                    // 2. Return null.                    return Ok(None);                }                // ii. Set lastIndex to AdvanceStringIndex(S, lastIndex, fullUnicode).                last_index = advance_string_index(input, last_index, unicode);            }            Some(m) => {                // c. If r is failure, then                #[allow(clippy::if_not_else)]                if m.start() != last_index {                    // i. If sticky is true, then                    if sticky {                        // 1. Perform ? Set(R, "lastIndex", +0????, true).                        this.set("lastIndex", 0, true, context)?;                        // 2. Return null.                        return Ok(None);                    }                    // ii. Set lastIndex to AdvanceStringIndex(S, lastIndex, fullUnicode).                    last_index = advance_string_index(input, last_index, unicode);                // d. Else,                } else {                    //i. Assert: r is a State.                    //ii. Set matchSucceeded to true.                    break m;                }            }        }    };    // 13. Let e be r's endIndex value.    let mut e = match_value.end();    // 14. If fullUnicode is true, then    if unicode {        // e is an index into the Input character list, derived from S, matched by matcher.        // Let eUTF be the smallest index into S that corresponds to the character at element e of Input.        // If e is greater than or equal to the number of elements in Input, then eUTF is the number of code units in S.        // b. Set e to eUTF.        e = input.split_at(e).0.encode_utf16().count();    }    // 15. If global is true or sticky is true, then    if global || sticky {        // a. Perform ? Set(R, "lastIndex", ????(e), true).        this.set("lastIndex", e, true, context)?;    }    // 16. Let n be the number of elements in r's captures List. (This is the same value as 22.2.2.1's NcapturingParens.)    let n = match_value.captures.len();    // 17. Assert: n < 23^2 - 1.    debug_assert!(n < 23usize.pow(2) - 1);    // 18. Let A be ! ArrayCreate(n + 1).    // 19. Assert: The mathematical value of A's "length" property is n + 1.    let a = Array::array_create(n + 1, None, context)?;    // 20. Perform ! CreateDataPropertyOrThrow(A, "index", ????(lastIndex)).    a.create_data_property_or_throw("index", match_value.start(), context)        .expect("this CreateDataPropertyOrThrow call must not fail");    // 21. Perform ! CreateDataPropertyOrThrow(A, "input", S).    a.create_data_property_or_throw("input", input.clone(), context)        .expect("this CreateDataPropertyOrThrow call must not fail");    // 22. Let matchedSubstr be the substring of S from lastIndex to e.    let matched_substr = if let Some(s) = input.get(match_value.range()) {        s    } else {        ""    };    // 23. Perform ! CreateDataPropertyOrThrow(A, "0", matchedSubstr).    a.create_data_property_or_throw(0, matched_substr, context)        .expect("this CreateDataPropertyOrThrow call must not fail");    // 24. If R contains any GroupName, then    // 25. Else,    let named_groups = match_value.named_groups();    let groups = if named_groups.clone().count() > 0 {        // a. Let groups be ! OrdinaryObjectCreate(null).        let groups = JsValue::from(JsObject::empty());        // Perform 27.f here        // f. If the ith capture of R was defined with a GroupName, then        // i. Let s be the CapturingGroupName of the corresponding RegExpIdentifierName.        // ii. Perform ! CreateDataPropertyOrThrow(groups, s, capturedValue).        for (name, range) in named_groups {            if let Some(range) = range {                let value = if let Some(s) = input.get(range.clone()) {                    s                } else {                    ""                };                groups                    .to_object(context)?                    .create_data_property_or_throw(name, value, context)                    .expect("this CreateDataPropertyOrThrow call must not fail");            }        }        groups    } else {        // a. Let groups be undefined.        JsValue::undefined()    };    // 26. Perform ! CreateDataPropertyOrThrow(A, "groups", groups).    a.create_data_property_or_throw("groups", groups, context)        .expect("this CreateDataPropertyOrThrow call must not fail");    // 27. For each integer i such that i ≥ 1 and i ≤ n, in ascending order, do    for i in 1..=n {        // a. Let captureI be ith element of r's captures List.        let capture = match_value.group(i);        let captured_value = match capture {            // b. If captureI is undefined, let capturedValue be undefined.            None => JsValue::undefined(),            // c. Else if fullUnicode is true, then            // d. Else,            Some(range) => {                if let Some(s) = input.get(range) {                    s.into()                } else {                    "".into()                }            }        };        // e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(????(i)), capturedValue).        a.create_data_property_or_throw(i, captured_value, context)            .expect("this CreateDataPropertyOrThrow call must not fail");    }    // 28. Return A.    Ok(Some(a))}

    Self::abstract_builtin_exec()方法中存在global以及last_index这样看来最终执行的方法就是在这里了,仔细查看该方法中的代码(代码写的很详细而且每一步都有注释)

    在第 12 步中:

    • lastIndex 超过文本长度且当 global 存在时将 lastIndex 置为 0

    • 获取匹配到的值(match_value

      如果未匹配到则置为advance_string_index()方法的返回值

      advance_string_index()不在当前问题的考虑范围 https://tc39.es/ecma262/#sec-...

    第 13 步获取匹配到的值的 endIndex

    第 15 步将 lastIndex 置为 endIndex

    至此也就整明白了g标志的含义,在正则的原型链中存在一个lastIndex,如果匹配为真时lastIndex不会重置为 0 ,下一次开始时继承了上次位置,

    结论

    在问题代码中分析

    const reg = /[a-z]/g; // 声明后,lastIndex 为 0reg.test('a'); // => true;第一次匹配后,lastIndex 为 1reg.test('a'); // => false;第二次匹配由于 lastIndex 为 1,且字符只有一个,得到 false,将 lastIndex 置为 0reg.test('a'); // => true;下面依次循环前两次的逻辑reg.test('a'); // => false;reg.test('a'); // => true;

    到此,相信大家对“JavaScript正则表达式中g标志实例分析”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    --结束END--

    本文标题: JavaScript正则表达式中g标志实例分析

    本文链接: https://lsjlt.com/news/325092.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    猜你喜欢
    • JavaScript正则表达式中g标志实例分析
      本篇内容主要讲解“JavaScript正则表达式中g标志实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript正则表达式中g标志实例分析”吧!有一天在思否社区看到有个问题,大...
      99+
      2023-06-29
    • JavaScript正则表达式中g标志详解
      目录缘起解密过程搜索引擎源码层面结论缘起 有一天在思否社区看到有个问题,大致描述如下 const list = ['a', 'b', '-', 'c', 'd']; const re...
      99+
      2024-04-02
    • JavaScript正则表达式实例分析
      这篇文章主要讲解了“JavaScript正则表达式实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript正则表达式实例分析”吧!1. 概...
      99+
      2024-04-02
    • JavaScript正则表达式使用实例分析
      本篇内容主要讲解“JavaScript正则表达式使用实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript正则表达式使用实例分析”吧!背景我有一个Node服务,希望对访问进来...
      99+
      2023-06-30
    • JavaScript正则表达式的示例分析
      这篇文章主要为大家展示了“JavaScript正则表达式的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JavaScript正则表达式的示例分析”这篇文...
      99+
      2024-04-02
    • Python正则表达式实例分析
      这篇“Python正则表达式实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Python正则表达式实例分析”文章吧。一...
      99+
      2023-06-29
    • linux 正则表达式grep实例分析
      在很多技术领域(如:自然语言处理,数据存储等),正则表达式可以很方便的提取我们想要的信息,所以正则表达式是一个很重要的知识点! 一、概念 正则表达式(Regular Expression)是用于描述一组字符串特征的模...
      99+
      2022-06-03
      linux 正则表达式 grep
    • Linux的正则表达式实例分析
      今天小编给大家分享一下Linux的正则表达式实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。正则表达式(regular...
      99+
      2023-06-27
    • Javascript常用正则表达式的示例分析
      小编给大家分享一下Javascript常用正则表达式的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!模式修饰符的可选参数...
      99+
      2024-04-02
    • javascript中正则表达式分组、断言的示例分析
      这篇文章主要为大家展示了“javascript中正则表达式分组、断言的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“javascript中正则表达式分组...
      99+
      2024-04-02
    • Python正则表达式使用实例分析
      这篇文章主要介绍“Python正则表达式使用实例分析”,在日常操作中,相信很多人在Python正则表达式使用实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python正则表达式使用实例分析”的疑惑有所...
      99+
      2023-06-08
    • Java正则表达式使用实例分析
      这篇文章主要介绍“Java正则表达式使用实例分析”,在日常操作中,相信很多人在Java正则表达式使用实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java正则表达式使用实例分析”的疑惑有所帮助!接下来...
      99+
      2023-06-29
    • Go语言正则表达式实例分析
      这篇文章主要介绍了Go语言正则表达式实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Go语言正则表达式实例分析文章都会有所收获,下面我们一起来看看吧。前言在计算中,我们经常需要将特定模式的字符或字符子集匹...
      99+
      2023-06-30
    • shell中正则表达式的示例分析
      这篇文章给大家分享的是有关shell中正则表达式的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。正则表达式匹配中文字符的正则表达式:[u4e00-u9fa5]评注:匹配中...
      99+
      2024-04-02
    • Java中正则表达式的示例分析
      这篇文章主要介绍了Java中正则表达式的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前几天线上一个项目监控信息突然报告异常,上到机器上后查看相关资源的使用情况,发现...
      99+
      2023-06-15
    • C#中正则表达式的示例分析
      这篇文章将为大家详细讲解有关C#中正则表达式的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。(1)“@”符号符下两ows表研究室的火热,当晨在“@”虽然并非C#正则表达式的“成员”,但是它经常与C...
      99+
      2023-06-18
    • mysql正则表达式的案例分析
      小编给大家分享一下mysql正则表达式的案例分析,希望大家阅读完这篇文章后大所收获,下面让我们一起去探讨吧!MySQL支持基于正则表达式和REGEXP运算符的另一种模式匹配操作。1.它提供了强大而灵活的模式...
      99+
      2024-04-02
    • jmeter正则表达式的示例分析
      小编给大家分享一下jmeter正则表达式的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!使用jmeter时经常有这样的情况:一个完整的操作流程,需先完成某个操作,获得某个值或数据信息,然后才能进行下一步的操作(也就...
      99+
      2023-06-14
    • VB.NET正则表达式的示例分析
      小编给大家分享一下VB.NET正则表达式的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.选择符 VB.NET正则表达式中“ &brvbar;”...
      99+
      2023-06-17
    • 正则表达式组的示例分析
      这篇文章主要为大家展示了“正则表达式组的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“正则表达式组的示例分析”这篇文章吧。正则表达式组的理解把正则表达式的一部分放在圆括号内,你可以将它们...
      99+
      2023-06-17
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作