【转】多语言的正则表达式,大家应该精晓

正则表明式,软件工程中最为强劲,且普遍适用,令人信服的技巧之一。从验证电子邮件地址到实施复杂的代码重构器,正则表明式的用途丰裕广泛,是其他软件工程师工具箱中须求的条目。

图片 1

怎么是正则表明式?

正则表明式(或Regex,或Regexp)是行使字符系列描述复杂搜索方式的一种办法。

可是,专门的Regex语法由于其复杂使得有些表明式变得不得访问。例如,上面的这些大旨的正则表明式,它意味着24钟头制HH
/ MM格式的年月。

\b([01]?[0-9]|2[0-3]):([0-5]\d)\b

一经你认为那看起来略显复杂,别担心,当大家成功那一个科目时,精通那么些说明式将会是小菜一碟。
Learn once, write anywhere

差点任何编程语言都可以行使Regex。Regex的学识对于注解用户输入,与Unix
shell举办互动,在您欣赏的公文编辑器中寻找/重构代码,执行数据库文本搜索等等都至极实用。

在本教程中,我将尝试在各个气象、语言和环境中对Regex的语法和动用进行简明易懂的牵线。

此Web应用程序是本身用于营造、测试和调试Regex最欣赏的工具。我强烈推荐大家利用它来测试大家将在本教程中介绍的表明式。

本课程中的示例源代码可以在Github存储库中找到——https://github.com/triestpa/You-Should-Learn-Regex
0 – 匹配任何数字行

大家将从一个万分简单的例证开头——匹配任何只含有数字的行。

^[0-9]+$

让我们一点一点的说明啊。

    ^ ——表示一行的发轫。
    [0-9] ——匹配0到9里边的数字
    + ——匹配前一个表达式的一个或三个实例。
    $ ——表示行尾。

大家得以用伪英文重写那些Regex为[start of line][one or more
digits][end of line]。

很简单,不是吗?

    大家可以用\d替换[0-9],结果一律(匹配所有数字)。

那些表明式(和一般的正则表明式)的赫赫之处在于它无需太多修改,就可以用到其它编程语言中。

为了演示,大家先快速了解怎么样利用16种最受欢迎的编程语言对文本文件实施此简单的Regex搜索。

俺们利用以下输入文件(test.txt)为例。

1234
abcde
12db2
5362

1

各类脚本都将利用那一个正则表达式读取并搜索test.txt文件,并将结果(’1234′,
‘5362’, ‘1’)输出到控制台。
语言范例
0.0 – Javascript / Node.js / Typescript
const fs = require(‘fs’)
const testFile = fs.readFileSync(‘test.txt’, ‘utf8’)
const regex = /^([0-9]+)$/gm
let results = testFile.match(regex)
console.log(results)

0.1 – Python
import re

with open(‘test.txt’, ‘r’) as f:
  test_string = f.read()
  regex = re.compile(r’^([0-9]+)$’, re.MULTILINE)
  result = regex.findall(test_string)
  print(result)

0.2 – R
fileLines <- readLines(“test.txt”)
results <- grep(“^[0-9]+$”, fileLines, value = TRUE)
print (results)

0.3 – Ruby
File.open(“test.txt”, “rb”) do |f|
    test_str = f.read
    re = /^[0-9]+$/m
    test_str.scan(re) do |match|
        puts match.to_s
    end
end

0.4 – Haskell
import Text.Regex.PCRE

main = do
  fileContents <- readFile “test.txt”
  let stringResult = fileContents =~ “^[0-9]+$” :: AllTextMatches []
String
  print (getAllTextMatches stringResult)

0.5 – Perl
open my $fh, ‘<‘, ‘test.txt’ or die “Unable to open file $!”;
read $fh, my $file_content, -s $fh;
close $fh;
my $regex = qr/^([0-9]+)$/mp;
my @matches = $file_content =~ /$regex/g;
print join(‘,’, @matches);

0.6 – PHP
<?php
$myfile = fopen(“test.txt”, “r”) or die(“Unable to open file.”);
$test_str = fread($myfile,filesize(“test.txt”));
fclose($myfile);
$re = ‘/^[0-9]+$/m’;
preg_match_all($re, $test_str, $matches, PREG_SET_ORDER, 0);
var_dump($matches);
?>

0.7 – Go
package main

import (
    “fmt”
    “io/ioutil”
    “regexp”
)

func main() {
    testFile, err := ioutil.ReadFile(“test.txt”)
    if err != nil { fmt.Print(err) }
    testString := string(testFile)
    var re = regexp.MustCompile(`(?m)^([0-9]+)$`)
    var results = re.FindAllString(testString, -1)
    fmt.Println(results)
}

0.8 – Java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;

class FileRegexExample {
  public static void main(String[] args) {
    try {
      String content = new
String(Files.readAllBytes(Paths.get(“test.txt”)));
      Pattern pattern = Pattern.compile(“^[0-9]+$”,
Pattern.MULTILINE);
      Matcher matcher = pattern.matcher(content);
      ArrayList<String> matchList = new
ArrayList<String>();

      while (matcher.find()) {
        matchList.add(matcher.group());
      }

      System.out.println(matchList);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

0.9 – Kotlin
import java.io.File
import kotlin.text.Regex
import kotlin.text.RegexOption

val file = File(“test.txt”)
val content:String = file.readText()
val regex = Regex(“^[0-9]+$”, RegexOption.MULTILINE)
val results = regex.findAll(content).map{ result -> result.value
}.toList()
println(results)

0.10 – Scala
import scala.io.Source
import scala.util.matching.Regex

object FileRegexExample {
  def main(args: Array[String]) {
    val fileContents =
Source.fromFile(“test.txt”).getLines.mkString(“\n”)
    val pattern = “(?m)^[0-9]+$”.r
    val results = (pattern findAllIn fileContents).mkString(“,”)
    println(results)
  }
}

0.11 – Swift
import Cocoa
do {
    let fileText = try String(contentsOfFile: “test.txt”, encoding:
String.Encoding.utf8)
    let regex = try! NSRegularExpression(pattern: “^[0-9]+$”, options:
[ .anchorsMatchLines ])
    let results = regex.matches(in: fileText, options: [], range:
NSRange(location: 0, length: fileText.characters.count))
    let matches = results.map { String(fileText[Range($0.range, in:
fileText)!]) }
    print(matches)
} catch {
    print(error)
}

0.12 – Rust
extern crate regex;
use std::fs::File;
use std::io::prelude::*;
use regex::Regex;

fn main() {
  let mut f = File::open(“test.txt”).expect(“file not found”);
  let mut test_str = String::new();
  f.read_to_string(&mut test_str).expect(“something went wrong
reading the file”);

  let regex = match Regex::new(r”(?m)^([0-9]+)$”) {
    Ok(r) => r,
    Err(e) => {
      println!(“Could not compile regex: {}”, e);
      return;
    }
  };

  let result = regex.find_iter(&test_str);
  for mat in result {
    println!(“{}”, &test_str[mat.start()..mat.end()]);
  }
}

0.13 – C#
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;

namespace RegexExample
{
    class FileRegexExample
    {
        static void Main()
        {
            string text = File.ReadAllText(@”./test.txt”,
Encoding.UTF8);
            Regex regex = new Regex(“^[0-9]+$”,
RegexOptions.Multiline);
            MatchCollection mc = regex.Matches(text);
            var matches = mc.OfType<Match>().Select(m =>
m.Value).ToArray();
            Console.WriteLine(string.Join(” “, matches));
        }
    }
}

0.14 – C++
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#include <regex>
using namespace std;

int main () {
  ifstream t(“test.txt”);
  stringstream buffer;
  buffer << t.rdbuf();
  string testString = buffer.str();

  regex numberLineRegex(“(^|\n)([0-9]+)($|\n)”);
  sregex_iterator it(testString.begin(), testString.end(),
numberLineRegex);
  sregex_iterator it_end;

  while(it != it_end) {
    cout << it -> str();
    ++it;
  }
}

0.15 – Bash
#!bin/bash
grep -E ‘^[0-9]+$’ test.txt

以十七种语言编写出相同的操作是一个有意思的演习,可是,接下去在本教程中,大家将重点运用Javascript和Python(最终还有一点Bash),因为那么些语言(在我眼里)倾向于暴发最明显和更可读的贯彻。
1 – 年份匹配

俺们来探视其它一个简约的例证——匹配二十或二十一世纪中其他有效的一年。

\b(19|20)\d{2}\b

我们利用\b而不是^和$来起首和终结这些正则表明式。\b表示单词边界,或三个单词之间的空格。那允许我们在文本块(而不是代码行)中至极年份,那对于搜索如段落文本非凡实惠。

    \b ——字边界
    (19|20) ——使用或(|)操作数匹配’19′或’20′。
    \d{2}——两位数,与[0-9]{2}相同
    \b ——字边界

   
请注意\b不同于\s,\s是用于空格字符的代码。\b搜索一个单词字符前边大概前边没有另一个字符的地点,因而它寻找单词字符的缺少,而\s明确搜索空格字符。\b尤其适用于我们想要匹配特定系列/单词的情事,而不是一定种类/单词此前或以后有空格的事态。

1.0 – 真实示例 – 计数年份
咱俩得以在Python脚本中选取此表明式来查找维基百科历史部分的稿子中提及20或21世纪内年份的次数。

import re
import urllib.request
import operator

# Download wiki page
url =
https://en.wikipedia.org/wiki/Diplomatic\_history\_of\_World\_War\_II
html = urllib.request.urlopen(url).read()

# Find all mentioned years in the 20th or 21st century
regex = r”\b(?:19|20)\d{2}\b”
matches = re.findall(regex, str(html))

# Form a dict of the number of occurrences of each year
year_counts = dict((year, matches.count(year)) for year in
set(matches))

# Print the dict sorted in descending order
for year in sorted(year_counts, key=year_counts.get, reverse=True):
  print(year, year_counts[year])

上述脚本将服从提及的次数依次打印年份。

1941 137
1943 80
1940 76
1945 73
1939 71

2 – 匹配时间
现行大家要定义一个正则表明式来协作24钟头格式(MM:HH,如16:59)的任哪一天间。

\b([01]?[0-9]|2[0-3]):([0-5]\d)\b

    \b——字边界
    [01]——0或1
    ?——表示上述形式是可选的。
    [0-9]—— 0到9以内的其余数字
    |——OR操作数
    2[0-3]——2,后边跟0和3时期的其余数字(即20-23)
    :——匹配:字符
    [0-5]——0到5里边的别样数字
    \d——0到9里面的任何数字(与[0-9]相同)
    \b ——字边界

2.0 – 捕获组
你只怕已经注意到上述方式中有了新情节—— 大家在括号 ( …
)中封装时辰和分钟的破获片段。那允许我们将方式的逐个部分概念为捕获组。

捕获组允许大家单独提取、转换和重新排列每一个匹配格局的一些。
2.1 – 真实示例 – 时间分析
比如,在上述24小时情势中,大家定义了三个捕获组—— 时和分。

俺们可以轻松地领到那一个捕获组。

以下是我们怎么使用Javascript将24小时制的时日分解成刻钟和分钟。

const regex = /\b([01]?[0-9]|2[0-3]):([0-5]\d)/
const str = `The current time is 16:24`
const result = regex.exec(str)
console.log(`The current hour is ${result[1]}`)
console.log(`The current minute is ${result[2]}`)

    第0个捕获组始终是全方位匹配表明式。

上述脚本将发生以下输出。

The current hour is 16
The current minute is 24

用作额外的教练,你可以尝试修改此脚本,将24钟头制转换为12钟头制(am/pm)。
3 – 匹配日期
今昔大家来合作一个DAY/MONTH/YEAR样式的日子情势。

\b(0?[1-9]|[12]\d|3[01])([\/\-])(0?[1-9]|1[012])\2(\d{4})

其一有点长,但它看起来与大家地点讲过的多少看似。

   
(0?[1-9]|[12]\d|3[01])——匹配1到31里面的别样数字(前面的0是可选的)
    ([\/\-])——匹配分隔符/或-
    (0?[1-9]|1[012])—— 匹配1到12中间的数字
    \2——匹配首个捕获组(分隔符)
    \d{4}——匹配任意4位数(0000 – 9999)

那边唯一新的定义是,我们利用\2来协作首个捕获组,即分隔符(/或-)。那使得大家可以防止重复形式匹配规范,并且须求分隔符是一样的(假若第二个分隔符是/,那么第一个分隔符也亟须一律)。
3.0 – 捕获组替换

通过行使捕获组,我们得以动态地整合和转换我们的字符串输入。

引用捕获组的规范方法是使用$或\标记,以及捕获组的目录(请牢记捕获组成分是全部的破获文本)。
3.1 – 真实示例 – 日期格式转换

设若我们的职务是将利用国际日期格式(DAY/MONTH/YEAR)的文档集合转换为美式(MONTH/DAY/YEAR)日期样式。

我们可以通过轮换方式$3$2$1$2$4或\3\2\1\2\4采取上述正则表明式。

让我们诠释捕捉组。

    $1——第二个捕获组:日期。
    $2——第四个捕捉组:分隔符。
    $3——第二个捕获组:月份。
    $4——第多少个捕获组:年份。

轮换格局(\3\2\1\2\4)简单地交流了表达式中月份和日期的始末。

以下是大家怎么在Javascript中展开那种转移:

const regex = /\b(0?[1-9]|[12]\d|3[01])([
\/\-])(0?[1-9]|1[012])\2(\d{4})/
const str = `Today’s date is 18/09/2017`
const subst = `$3$2$1$2$4`
const result = str.replace(regex, subst)
console.log(result)

上述脚本将打印Today’s date is 09/18/2017到控制台。

平等的剧本在Python中是这么的:

import re
regex = r’\b(0?[1-9]|[12]\d|3[01])([
\/\-])(0?[1-9]|1[012])\2(\d{4})’
test_str = “Today’s date is 18/09/2017”
subst = r’\3\2\1\2\4′
result = re.sub(regex, subst, test_str)
print(result)

4 – 电子邮件验证
正则表达式也可用来输入验证。

^[^@\s]+@[^@\s]+\.\w{2,6}$

以上是一个(过于简短的)Regex,用来同盟电子邮件地址。

    ^——输入开始
    [^@\s]——匹配除@和空格\s之外的其余字符
    +——1+次数
    @——匹配’@’符号
    [^@\s]+——匹配除@和空格之外的其余字符,1+次数
    \.——匹配’.’字符。
    \w{2,6}——匹配任何字符(字母,数字或下划线),2-6次
    $——输入完成

4.0 – 真实示例 – 验证电子邮件

如果大家要创建一个简单的Javascript函数以检讨输入是还是不是为可行的电子邮件。

function isValidEmail (input) {
  const regex = /^[^@\s]+@[^@\s]+\.\w{2,6}$/g;
  const result = regex.exec(input)

  // If result is null, no match was found
  return !!result
}

const tests = [
  `test.test@gmail.com`, // Valid
  ”, // Invalid
  `test.test`, // Invalid
  ‘@invalid@test.com’, // Invalid
  ‘invalid@@test.com’, // Invalid
  `gmail.com`, // Invalid
  `this is a test@test.com`, // Invalid
  `test.test@gmail.comtest.test@gmail.com` // Invalid
]

console.log(tests.map(isValidEmail))

此脚本的出口应为[ true, false, false, false, false, false, false, false
]。

   
注意——在切切实实应用程序中,使用Regex验证电子邮件地址对于众多处境,例如用户注册,是不够的。可是只要您肯定输入的公文是电子邮件地址,那么你应有一向按照发送确认/激活电子邮件的科班做法。

4.1 – 完整的电子邮件Regex
那是一个分外简单的例子,它忽略了无数可怜首要的电子邮件有效性边缘情形,例如无效的初阶/甘休字符以及再而三的周期。我确实不指出在你的应用程序中使用上述表明式;最好是运用一个闻名声的电子邮件验证库或接续深究更完整的电子邮件验证Regex。

譬如,上面是一个来源于emailregex.com的更尖端的表明式,它万分99%的RFC
5322一双两好的电子邮件地址。

(?:[a-z0-9!#$%&’*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&’*+/=?^_`{|}~-]+)*|”(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*”)@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

而是明日我们不打算深入商讨。
5 – 代码注释方式匹配
Regex最可行的特有用法之一是能够成为代码重构器。一大半代码编辑器帮忙基于Regex的追寻/替换操作。一个格式正确的Regex替换可以将繁琐的内需半钟头忙绿的办事变成一个可以的Regex重构魔法。

无须编写脚本来执行这个操作,试着在你挑选的文书编辑器中去做。几乎逐个文本编辑器都协理基于Regex的追寻和替换。

以下是有些受欢迎的编辑器指南。

Sublime中的Regex替换——http://docs.sublimetext.info/en/latest/search\_and\_replace/search\_and\_replace\_overview.html\#using-regular-expressions-in-sublime-text

Vim中的Regex替换——http://vimregex.com/\#backreferences

VSCode中的Regex替换——https://code.visualstudio.com/docs/editor/codebasics\#\_advanced-search-options

Emacs中的Regex替换——https://www.gnu.org/software/emacs/manual/html\_node/emacs/Regexp-Replace.html
5.0 – 提取单行CSS注释

若是我们想要查找CSS文件中的所有单行注释如何是好?

CSS注释以/* Comment Here */的格式出现。

要捕获任何单行CSS注释,我们得以行使以下表明式。

(\/\*+)(.*)(\*+\/)

    \/——匹配/符号(我们有转义/字符)
    \*+——匹配一个或三个*标志(再一次,大家运用\来转义*字符)。
    (.*)——匹配任何字符(除了换行符\n),任意次数
    \*+——匹配一个或七个*字符
    \/——匹配关闭/符号。

留神,大家曾经在上头的表明式中定义了三个捕获组:开放字符((\/\*+)),注释内容((.*))和得了字符((\*+\/))。
5.1 – 真实示例 – 将单行注释转换为多行注释

俺们可以动用此表达式通过实践以下替换将单行注释转换为多行注释。

$1\n$2\n$3

在此间,大家只是在种种捕获组之间添加了一个换行符\n。

品味在有以下内容的文书上推行此替换。

/* Single Line Comment */
body {
  background-color: pink;
}

/*
 Multiline Comment
*/
h1 {
  font-size: 2rem;
}

/* Another Single Line Comment */
h2 {
  font-size: 1rem;
}

轮换将生出同样的文件,但各样单行注释转换为多行注释。

/*
 Single Line Comment
*/
body {
  background-color: pink;
}

/*
 Multiline Comment
*/
h1 {
  font-size: 2rem;
}

/*
 Another Single Line Comment
*/
h2 {
  font-size: 1rem;
}

5.2 – 真实示例 – 标准化CSS注释起先

只要咱们有一个又大又繁杂的CSS文件,是由几个例外的人写的。在那一个文件中,有些注释以/*开头,有些以/**起来,还有些以/*****开头。

让我们来写一个Regex替换以规则所有的单行CSS注释,以/*开头。

为了做到那或多或少,大家将扩充表达式,只匹配以多少个或越来越多星号开首的诠释。

(\/\*{2,})(.*)(\*+\/)

本条表明式与原来的极度相似。首要不同在于发轫我们用\*{2,}替换了\*+。\*{2,}语法表示*的“多个或多个”实例。

为了规范每一种注释的先河,大家得以由此以下替代。

/*$2$3

让我们在偏下测试CSS文件上运行此替换。

/** Double Asterisk Comment */
body {
  background-color: pink;
}

/* Single Asterisk Comment */
h1 {
  font-size: 2rem;
}

/***** Many Asterisk Comment */
h2 {
  font-size: 1rem;
}

结果将是与正统注释开首相同的文书。

/* Double Asterisk Comment */
body {
  background-color: pink;
}

/* Single Asterisk Comment */
h1 {
  font-size: 2rem;
}

/* Many Asterisk Comment */
h2 {
  font-size: 1rem;
}

6 – 匹配网址
另一个非常管用的Regex是在文书中匹配URL。

上边是一个出自Stack Overflow的URL匹配表达式的演示。

(https?:\/\/)(www\.)?(?<domain>[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6})(?<path>\/[-a-zA-Z0-9@:%_\/+.~#?&=]*)?

    (https?:\/\/)——匹配http(s)
    (www\.)?——可选的“www”前缀
    (?<domain>[-a-zA-Z0-9@:%._\+~#=]{2,256}——匹配有效的域名
    \.[a-z]{2,6})——匹配域扩充扩充名(即“.com”或“.org”)
   
(?<path>\/[-a-zA-Z0-9@:%_\/+.~#?&=]*)?——匹配URL路径(/posts)、查询字符串(?limit=1)和/或文件扩张名(.html),这几个都是可选的。

6.0 – 命名捕获组
您放在心上到没有,一些抓获组现在以?<name>标识符开始。那是命名捕获组的语法,可以使得数据提取越发清晰。
6.1 – 真实示例 – 从Web页面上的URL解析域名
以下是大家什么使用命名捕获组来提取使用Python语言的网页中各类URL的域名。

import re
import urllib.request

html = str(urllib.request.urlopen(“https://moz.com/top500").read())
regex =
r”(https?:\/\/)(www\.)?(?P<domain>[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6})(?P<path>\/[-a-zA-Z0-9@:%_\/+.~#?&=]*)?”
matches = re.finditer(regex, html)

for match in matches:
  print(match.group(‘domain’))

本子将打印在原始网页HTML内容中找到的各类域名。


facebook.com
twitter.com
google.com
youtube.com
linkedin.com
wordpress.org
instagram.com
pinterest.com
wikipedia.org
wordpress.com

7 – 命令行的用法

不少Unix命令行实用程序也支持Regex!大家将介绍怎样使用grep查找特定文件,以及拔取sed替换文本文件内容。
7.0 – 真实示例 – 用grep匹配图像文件
大家将概念另一个主导的Regex,本次是用来匹配图像文件。
^.+\.(?i)(png|jpg|jpeg|gif|webp)$

    ^——开始行。
   
.+——匹配任何字符(字母,数字,符号),除了\n(换行)之外,1+次数。
    \.——匹配 ‘.’字符。
    (?i)——表示下一个队列不区分轻重缓急写。
    (png|jpg|jpeg|gif|webp)——匹配常见的图像文件伸张名
    $——结束行

以下是何等列出Downloads目录中所有图像文件的办法。

ls ~/Downloads | grep -E ‘^.+\.(?i)(png|jpg|jpeg|gif|webp)$’

    ls ~/Downloads——列出Downloads目录中的文件
    |——将出口管道输送到下一个指令
    grep -E——使用正则表达式过滤输入

7.1 – 真实事例 – 用sed举办电子邮件替换

bash命令中正则表明式的另一个便宜是在文件文件中修改电子邮件。

那可以透过选拔sed命令以及前边的电子邮件Regex的改动版本达成。

sed -E -i ‘s/^(.*?\s|)[^@]+@[^\s]+/\1\{redacted\}/g’
test.txt

    sed——Unix的“流编辑器”实用程序,允许强大的公文文件转换。
    -E——使用增加的Regex情势匹配
    -i——原位替换文件流
    ‘s/^(.*?\s|)——将行的上马包装在破获组中
    [^@]+@[^\s]+——电子邮件Regex的简化版本。
    /\1\{redacted\}/g’——用{redacted}替换各种电子邮件地址。
    test.txt——对test.txt文件执行操作。

我们得以在一个演示test.txt文件上运行方面的交替命令。

My email is patrick.triest@gmail.com

命令运行后,电子邮件将从test.txt文件中开展编制。

My email is {redacted}

提个醒——此命令将自行从你传递的其余test.txt中删去所有电子邮件地址,由此,在运行它的时候要小心,因为此操作不或许翻盘。要在终点中预览结果,而不是替换原来的文书,只需省略-i标志。

在意——即使上述命令适用于一大半Linux发行版,可是macOS使用BSD完结是sed,它在其扶助的Regex语法中备受越来越多的限制。要在MacOS上选用sed,并保有光荣的正则表明式帮衬,我提议采纳brew
install gnu-sed安装sed的GNU完成,然后从命令行使用gsed而不是sed。
8 – 哪一天不拔取Regex

好的,知道Regex是一个强硬又利落的工具了啊?!那么,有没有相应防止编制Regex的时候?有!
8.0 – 语言分析

分析结构化语言,从匈牙利(Magyarország)语到Java到JSON,使用正则表明式都是一种真正的惨痛。

当数据源中的边缘意况或附带语法错误导致表达式战败时,将招致最终(或立刻)的灾害,出于此目标去编写你自个儿的正则表明式或然会让你心境悲伤。

深化的解析器差不多可用于具有机器可读的语言,而NLP工具可用于人类语言——我强烈指出你利用其中一种,而不是尝尝编写自个儿的言语。
8.1 – 安全 – 输入过滤和黑名单

使用Regex过滤用户输入(例世尊自Web表单),以及预防黑客向应用程序发送恶意指令(例如SQL注入),看上去如同很诱人。

在那边运用自定义的Regex是不明智的,因为它很难覆盖各个潜在的攻击向量或恶意指令。例如,黑客可以应用替代字符编码绕过编写得不周详的输入黑名单过滤器。

那是另一个实例,对此我强烈提议你使用经过精美测试的库和/或服务,以及选取白名单而不是黑名单,以维护你的应用程序免受恶意输入。
8.2 – 品质密集的应用程序

正则表明式的匹配速度能够没有是非常快到极慢的界定变更,取决于表明式写得怎么样。对于大多数用例来说,那很好,更加是一旦匹配的文本十分短(例如电子邮件地址表单)的话。不过,对于高质量服务器应用程序,正则表明式会化为质量瓶颈,更加是要是表明式写得倒霉或被搜寻的文本非常短的话。
8.3 – 对于不须要Regex的地点

正则表明式是一个非凡管用的工具,但那并不意味你应有在其余地点采纳它。

假定难点有顶替的化解方案,化解方案更简明和/或不必要采纳Regex,那么请不要只是为了表现而使用Regex。Regex很棒,但它也是最不可读的编程工具之一,而且很简单并发边缘情状和bug。

超负荷施用Regex会让您的同事(以及须求工作在你的代码上的任哪个人)生气恼怒,甚至恨不得揍你一顿。
结论

自我梦想那是对Regex的不少用场的一个使得的牵线。

还有众多Regex的用例是我们从不包罗的。例如,可以在PostgreSQL查询中采取Regex来动态地寻找数据库中的文本形式。

咱俩还漏下了累累强有力的Regex语法性情没有覆盖,如lookahead,lookbehind,atomic
groups,recursion和subroutines。

要提升正则表明式技能并问询有关那几个作用的越多新闻,我推荐以下资源。

    Learn Regex The Easy Way – https://github.com/zeeshanu/learn-regex
    Regex101 – https://regex101.com/
    HackerRank Regex Course –
https://www.hackerrank.com/domains/regex/re-introduction

本学科中示范的源代码可以在Github存储库中找到——
https://github.com/triestpa/You-Should-Learn-Regex

欢迎随时对本学科提出任何提议、看法或针砭时弊。