Ray 的记录站

日常开发实践记录

0%

正则表达式 Checklist

正则表达式一直都是开发人员的必备技能,有人甚至提出在学习开发前先要熟练掌握正则表达式,可见其重要性。

这次利用实现 GA 数据分析的机会,整理了一个 Checklist 方便查阅(APP 上传的事件数据由于没有统一规定,内容千奇百怪,利用正则抽取其中关注的部分)。

根据 Free Code Camp 的教程来写的,原示例基于 JS 语言.

JS 中的正则

常用方法

  1. 基本 => 正则表达式的创建:JS 中正则模式使用 // 来定义, 因此不需要使用双引号。
  2. 方法 => test: 使用 Test 方法 .test() 用于检查字符串是否匹配模式,如果是返回 true, regEx.test(someString)
  3. 方法 => match: match 方法用于检测并返回匹配的字符串,比如 "Hello, World!".match(/Hello/); 返回 ["Hello"]
  4. 注意 => match 和 test 的对象: test 是正则对象的方法, match 是字符串对象的方法.

常量匹配

常量匹配 => 大小写敏感:直接搜索常量,且默认匹配大小写,即 const testRegex = /Kevin/.

正则标志

  1. 标志 => i: 忽略大小写匹配, 使用 i 标志, 如: /someString/i
  2. 标志 => g: 整串搜索匹配, 使用 g 标志(没有使用该标志则仅返回第一个匹配的子串)
    1
    2
    3
    let testStr = "Repeat, Repeat, Repeat";
    let ourRegex = /Repeat/g;
    testStr.match(ourRegex); // 返回 ["Repeat", "Repeat", "Repeat"]

单字符匹配

  1. 单字符匹配 => .: 使用点通配符匹配一个任意字符
    /hu./ 能匹配 hug, huh, huu
  2. 单字符匹配 => 字符集(character class) []: 匹配集合中的某个字符, 比如 /b[aiu]g/ 能够匹配 bag, big, bug
  3. 单字符匹配 => 字符集范围: 使用 - 指定字符集范围, 匹配范围内的任意一个字符(包含上下界的字符), 比如 /[a-e]at/ 能够匹配 aat, bat, cat, dat, eat
  4. 单字符匹配 => 匹配范围内的字母或数字: 可以像这样定义 [a-z0-9], 比如 /[a-z0-9]/ig; 用在 Jenny8675309 上可以匹配所有的单个字符, 如果使用 match 方法则返回的是该字符串的所有字符.
  5. 单字符匹配 => 排除: 使用 ^ 可定义排除字符集, 比如 /aeiou/ig 匹配单个非元音字符(但这样写的话所有 ., ! 这些非字母字符也会被匹配到, 甚至是空格, 因只排除了元音字母)
    1
    2
    3
    let quoteSample = "3 blind mice.";
    let myRegex = /[^aeiou0-9]/ig; // 匹配排除元音和数字的其他所有字符, 且整串搜索忽略大小写
    let result = quoteSample.match(myRegex); // 返回所有 9 个匹配的字符: 空格 b l n d 空格 m c .
  6. 单字符匹配 => \w: 匹配任意字母数字或下划线, 相当于 [A-Za-z0-9_] 的简化写法
    • /\w/g 用在 are you 上返回 ["a", "r", "e", "y", "o", "u"]
  7. 单字符匹配 => \W: 匹配任意非字母数字和下划线, 相当于 [^A-Za-z0-9_]
    • /\W/ 用在 42% 上返回 ["%"]
  8. 单字符匹配 => \d: 匹配数字, 相当于 [0-9]
  9. 单字符匹配 => \D: 匹配非数字, 相当于 [^0-9]
  10. 单字符匹配 => \s: 匹配空白符, 包括空格, 换行, tab, form feed 等, 相当于 [\r\n\t\f\v]
  11. 单字符匹配 => \S: 匹配非空白符, 相当于 [^\r\t\f\n\v]

多字符匹配

  1. 多字符匹配 => +: 匹配出现一次或多次, 比如 /a+/g, 用在 abc 上返回 ["a"], 用在 aaabc 上返回 ["aaa"], 用在 abab 上返回 ["a", "a"]
  2. 多字符匹配 => *: 匹配出现 0 次或多次, 比如 /go*/, 用在 "gooooooooal!" 上返回 ["goooooooo"], 用在 gut feeling 上返回 ["g"](u匹配 0 次或多次), 用在 over the moon 上返回 null
  3. 多字符匹配 => 贪心匹配和懒匹配:
    • 利用 * 可以进行贪心匹配(最长匹配), /t[a-z]*i/ 用在 "titanic" 返回 ["titani"]
    • 利用 ? 可以进行懒匹配(最短匹配), /t[a-z]*?i/ 用在 "titanic" 返回 ["ti"]
    • 利用 ? 还可以表达”可以匹配, 也可以不匹配”, 比如 /favou?rite/ 可以匹配 favoritefavourite
  4. 多字符匹配 => ^: 匹配字符串开头, 比如 /^Ricky/
    • 用在 "Ricky is first and can be found." 返回 ["Ricky"]
    • 用在 "You can't find Ricky now." 返回 null
  5. 多字符匹配 => $: 匹配字符串结尾, 比如 /story$/
    • 用在 "This is a never ending story" 返回 ["story"]
    • 用在 "Sometimes a story will have to end" 返回 null
  6. 多字符匹配 => \w+: 匹配任意一个或多个字母数字或下划线, 比如 /\w+/
    • 用在 "42" 返回 ["42"]
    • 用在 "important_var" 返回 ["important_var"]
    • 用在 "are you" 返回 ["are", "you"]
  7. 多字符匹配 => \W+: 匹配连续多个非字母数字下划线字符
  8. 多字符匹配 => \d+: 匹配连续多个数字
  9. 多字符匹配 => \D+: 匹配连续多个非数字字符

特殊操作

  1. 或操作符 |: 比如 /^[a-z][a-z]+\d*$|^[a-z]\d\d+$/i 中就是或操作符, 操作符两侧优先按左侧匹配, 未匹配则再看右侧

  2. 出现频次限定: 比如 /a{3,5}h/ 可以匹配 aaaah, 但不能匹配 aah

    • {2,}(仅下限)
    • {3,5}(上下限)
    • {3}(固定次数)
  3. 正则表达式中允许出现括号 () 以包括多个模式, 比如 /^[a-z]([0-9]{2,}|[a-z]+\d*)$/i; 中限定末尾匹配的相关表达.

  4. Lookahead: 前瞻, 即仅判断是否满足指定正则, 但不进行后续匹配, 当想在一个字符串中使用多种正则查找时非常有用.

    • positive: (?=...) 其中 ... 是具体正则, positive 会根据指定的正则确认匹配是否存在, 但不匹配该正则.
    • negative: (?!...) 其中 ... 是具体正则, negative 会根据指定的正则确认匹配是否不存在, 但不匹配该正则.

    例如给一个字符串 "qu":

    • 如果使用 /q(?=u)/ 进行匹配则返回 q(匹配到存在的位置, 但不进行后面的匹配)
    • 如果使用 /q(?!t)/, 也仍会返回 q(匹配到不存在的位置, 但不进行后面的匹配)
  5. 使用 | 配合 () 可以匹配一组正则: 比如 /P(engu|umpk)in/ 可以匹配 PenguinPumpkin

  6. Reuse Patterns Using Capture Groups: 使用括号包含的 (\w+) \1, 即利用 capture group, 通过编号在后面进行引用.

  7. Use Capture Groups to Search and Replace: 使用 replace 方法, 利用 capture group 捕捉到的字符串进行替换, 或者是将匹配到的内容替换为新的字符串.

  8. Remove Whitespace from Start and End: 这个练习中识别开头和结尾的连续空白, 并用 replace 替换为空, 即可去掉所有空白.

Python

  1. compile 方法创建正则表达式对象

  2. 匹配 import 语句:

    1
    2
    3
    4
    5
    6
    7
    8
    [
    ('#import ', '<', 'Foundation/', 'Foundation.h', '>'),
    ('#import ', '<', 'sqlitemanager/', 'FMDatabase.h', '>'),
    ('#import ', '<', 'sqlitemanager/', 'FMResultSet.h', '>'),
    ('#import ', '<', 'sqlitemanager/', 'FMDatabaseAdditions.h', '>'),
    ('#import ', '<', 'sqlitemanager/', 'FMDatabaseQueue.h', '>'),
    ('#import ', '<', 'sqlitemanager/', 'FMDatabasePool.h', '>')
    ]

    可以使用这个:

    1
    2
    3
    import_regex = r'^(#import\s)(<)(\w+\/)*(\w+\.h)(>)(\s*)$'
    match_import_regex = re.compile(import_regex, re.IGNORECASE | re.MULTILINE)
    result = match_import_regex.sub(r'\1"\4"\6', content)
  3. 抽取关注的内容, 如:

    1
    '["ver": "1.2.34.56", "id": " xxxxx-xxxxx-xxxxx-xxxxx-xxxx-- --2021-10-08 02:16:05 +0000,\\n sysV:11.6.0 Arm64"]'
    1
    2
    3
    4
    ver_regex = r'("ver":\s*)"((\d+\.)*\d+)"'
    id_regex = r'([A-Z0-9]{5}-){4}([A-Z0-9]{4})'
    event_date_regex = r'(\d{4}(-\d{2}){2}) ((\d{2}:*)*) \+\d{4}'
    sys_info_regex = r'sysV:(\d+.*?)\s(\w+),'