日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转】多语言的正则表达式,我们应该掌握

發(fā)布時間:2025/3/21 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】多语言的正则表达式,我们应该掌握 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

正則表達(dá)式,軟件工程中最為強(qiáng)大,且廣泛適用,令人信服的技術(shù)之一。從驗證電子郵件地址到執(zhí)行復(fù)雜的代碼重構(gòu)器,正則表達(dá)式的用途非常廣泛,是任何軟件工程師工具箱中必不可少的條目。

什么是正則表達(dá)式?

正則表達(dá)式(或Regex,或Regexp)是使用字符序列描述復(fù)雜搜索模式的一種方式。

然而,專門的Regex語法由于其復(fù)雜性使得有些表達(dá)式變得不可訪問。例如,下面的這個基本的正則表達(dá)式,它表示24小時制HH / MM格式的時間。

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

如果你覺得這看上去略顯復(fù)雜,別擔(dān)心,當(dāng)我們完成這個教程時,理解這個表達(dá)式將會是小菜一碟。
Learn once, write anywhere

幾乎任何編程語言都可以使用Regex。Regex的知識對于驗證用戶輸入,與Unix shell進(jìn)行交互,在你喜歡的文本編輯器中搜索/重構(gòu)代碼,執(zhí)行數(shù)據(jù)庫文本搜索等等都非常有用。

在本教程中,我將嘗試在各種場景、語言和環(huán)境中對Regex的語法和使用進(jìn)行簡明易懂的介紹。

此Web應(yīng)用程序是我用于構(gòu)建、測試和調(diào)試Regex最喜歡的工具。我強(qiáng)烈推薦大家使用它來測試我們將在本教程中介紹的表達(dá)式。

本教程中的示例源代碼可以在Github存儲庫中找到——https://github.com/triestpa/You-Should-Learn-Regex
0 – 匹配任何數(shù)字行

我們將從一個非常簡單的例子開始——匹配任何只包含數(shù)字的行。

^[0-9]+$

讓我們一點一點的解釋吧。

??? ^ ——表示一行的開始。
??? [0-9] ——匹配0到9之間的數(shù)字
??? + ——匹配前一個表達(dá)式的一個或多個實例。
??? $ ——表示行尾。

我們可以用偽英文重寫這個Regex為[start of line][one or more digits][end of line]。

很簡單,不是嗎?

??? 我們可以用\d替換[0-9],結(jié)果相同(匹配所有數(shù)字)。

這個表達(dá)式(和一般的正則表達(dá)式)的偉大之處在于它無需太多修改,就可以用到任何編程語言中。

為了演示,我們先快速了解如何使用16種最受歡迎的編程語言對文本文件執(zhí)行此簡單的Regex搜索。

我們使用以下輸入文件(test.txt)為例。

1234
abcde
12db2
5362

1

每個腳本都將使用這個正則表達(dá)式讀取并搜索test.txt文件,并將結(jié)果('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

以十六種語言編寫出相同的操作是一個有趣的練習(xí),但是,接下來在本教程中,我們將主要使用Javascript和Python(最后還有一點Bash),因為這些語言(在我看來)傾向于產(chǎn)生最清晰和更可讀的實現(xiàn)。
1 – 年份匹配

我們來看看另外一個簡單的例子——匹配二十或二十一世紀(jì)中任何有效的一年。

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

我們使用\b而不是^和$來開始和結(jié)束這個正則表達(dá)式。\b表示單詞邊界,或兩個單詞之間的空格。這允許我們在文本塊(而不是代碼行)中匹配年份,這對于搜索如段落文本非常有用。

??? \b ——字邊界
??? (19|20) ——使用或(|)操作數(shù)匹配’19′或’20′。
??? \d{2}——兩位數(shù),與[0-9]{2}相同
??? \b ——字邊界

??? 請注意\b不同于\s,\s是用于空格字符的代碼。\b搜索一個單詞字符前面或者后面沒有另一個字符的地方,因此它搜索單詞字符的缺失,而\s明確搜索空格字符。\b特別適用于我們想要匹配特定序列/單詞的情況,而不是特定序列/單詞之前或之后有空格的情況。

1.0 – 真實示例 – 計數(shù)年份
我們可以在Python腳本中使用此表達(dá)式來查找維基百科歷史部分的文章中提及20或21世紀(jì)內(nèi)年份的次數(shù)。

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])

上述腳本將按照提及的次數(shù)依次打印年份。

1941 137
1943 80
1940 76
1945 73
1939 71
...

2 – 匹配時間
現(xiàn)在我們要定義一個正則表達(dá)式來匹配24小時格式(MM:HH,如16:59)的任何時間。

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

??? \b——字邊界
??? [01]——0或1
??? ?——表示上述模式是可選的。
??? [0-9]—— 0到9之間的任何數(shù)字
??? |——OR操作數(shù)
??? 2[0-3]——2,后面跟0和3之間的任何數(shù)字(即20-23)
??? :——匹配:字符
??? [0-5]——0到5之間的任何數(shù)字
??? \d——0到9之間的任何數(shù)字(與[0-9]相同)
??? \b ——字邊界

2.0 – 捕獲組
你可能已經(jīng)注意到上述模式中有了新內(nèi)容—— 我們在括號 ( ... )中封裝小時和分鐘的捕獲片段。這允許我們將模式的每個部分定義為捕獲組。

捕獲組允許我們單獨提取、轉(zhuǎn)換和重新排列每個匹配模式的片段。
2.1 – 真實示例 – 時間分析
例如,在上述24小時模式中,我們定義了兩個捕獲組—— 時和分。

我們可以輕松地提取這些捕獲組。

以下是我們?nèi)绾问褂肑avascript將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個捕獲組始終是整個匹配表達(dá)式。

上述腳本將產(chǎn)生以下輸出。

The current hour is 16
The current minute is 24

作為額外的訓(xùn)練,你可以嘗試修改此腳本,將24小時制轉(zhuǎn)換為12小時制(am/pm)。
3 – 匹配日期
現(xiàn)在我們來匹配一個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之間的任何數(shù)字(前面的0是可選的)
??? ([\/\-])——匹配分隔符/或-
??? (0?[1-9]|1[012])—— 匹配1到12之間的數(shù)字
??? \2——匹配第二個捕獲組(分隔符)
??? \d{4}——匹配任意4位數(shù)(0000 – 9999)

這里唯一新的概念是,我們使用\2來匹配第二個捕獲組,即分隔符(/或-)。這使得我們能夠避免重復(fù)模式匹配規(guī)范,并且要求分隔符是一致的(如果第一個分隔符是/,那么第二個分隔符也必須一樣)。
3.0 – 捕獲組替換

通過使用捕獲組,我們可以動態(tài)地重組和轉(zhuǎn)換我們的字符串輸入。

引用捕獲組的標(biāo)準(zhǔn)方法是使用$或\符號,以及捕獲組的索引(請記住捕獲組元素是完整的捕獲文本)。
3.1 – 真實示例 – 日期格式轉(zhuǎn)換

假設(shè)我們的任務(wù)是將使用國際日期格式(DAY/MONTH/YEAR)的文檔集合轉(zhuǎn)換為美式(MONTH/DAY/YEAR)日期樣式。

我們可以通過替換模式$3$2$1$2$4或\3\2\1\2\4使用上述正則表達(dá)式。

讓我們分解捕捉組。

??? $1——第一個捕獲組:日期。
??? $2——第二個捕捉組:分隔符。
??? $3——第三個捕獲組:月份。
??? $4——第四個捕獲組:年份。

替換模式(\3\2\1\2\4)簡單地交換了表達(dá)式中月份和日期的內(nèi)容。

以下是我們?nèi)绾卧贘avascript中進(jìn)行這種轉(zhuǎn)換:

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 – 電子郵件驗證
正則表達(dá)式也可用于輸入驗證。

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

以上是一個(過于簡單的)Regex,用來匹配電子郵件地址。

??? ^——輸入開始
??? [^@\s]——匹配除@和空格\s之外的任何字符
??? +——1+次數(shù)
??? @——匹配’@'符號
??? [^@\s]+——匹配除@和空格之外的任何字符,1+次數(shù)
??? \.——匹配’.'字符。
??? \w{2,6}——匹配任何字符(字母,數(shù)字或下劃線),2-6次
??? $——輸入結(jié)束

4.0 – 真實示例 – 驗證電子郵件

假設(shè)我們要創(chuàng)建一個簡單的Javascript函數(shù)以檢查輸入是否為有效的電子郵件。

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))

此腳本的輸出應(yīng)為[ true, false, false, false, false, false, false, false ]。

??? 注意——在現(xiàn)實應(yīng)用程序中,使用Regex驗證電子郵件地址對于許多情況,例如用戶注冊,是不夠的。但是一旦你確認(rèn)輸入的文本是電子郵件地址,那么你應(yīng)該始終遵循發(fā)送確認(rèn)/激活電子郵件的標(biāo)準(zhǔn)做法。

4.1 – 完整的電子郵件Regex
這是一個非常簡單的例子,它忽略了許多非常重要的電子郵件有效性邊緣情況,例如無效的開始/結(jié)束字符以及連續(xù)的周期。我真的不建議在你的應(yīng)用程序中使用上述表達(dá)式;最好是使用一個有信譽(yù)的電子郵件驗證庫或繼續(xù)探索更完整的電子郵件驗證Regex。

例如,下面是一個來自emailregex.com的更高級的表達(dá)式,它匹配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最有用的特殊用法之一是可以成為代碼重構(gòu)器。大多數(shù)代碼編輯器支持基于Regex的查找/替換操作。一個格式正確的Regex替換可以將繁瑣的需要半小時忙碌的工作變成一個漂亮的Regex重構(gòu)魔法。

不要編寫腳本來執(zhí)行這些操作,試著在你選擇的文本編輯器中去做。幾乎每個文本編輯器都支持基于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 */的格式出現(xiàn)。

要捕獲任何單行CSS注釋,我們可以使用以下表達(dá)式。

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

??? \/——匹配/符號(我們有轉(zhuǎn)義/字符)
??? \*+——匹配一個或多個*符號(再次,我們使用\來轉(zhuǎn)義*字符)。
??? (.*)——匹配任何字符(除了換行符\n),任意次數(shù)
??? \*+——匹配一個或多個*字符
??? \/——匹配關(guān)閉/符號。

注意,我們已經(jīng)在上面的表達(dá)式中定義了三個捕獲組:開放字符((\/\*+)),注釋內(nèi)容((.*))和結(jié)束字符((\*+\/))。
5.1 – 真實示例 – 將單行注釋轉(zhuǎn)換為多行注釋

我們可以使用此表達(dá)式通過執(zhí)行以下替換將單行注釋轉(zhuǎn)換為多行注釋。

$1\n$2\n$3

在這里,我們只是在每個捕獲組之間添加了一個換行符\n。

嘗試在有以下內(nèi)容的文件上執(zhí)行此替換。

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

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

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

替換將產(chǎn)生相同的文件,但每個單行注釋轉(zhuǎn)換為多行注釋。

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

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

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

5.2 – 真實示例 – 標(biāo)準(zhǔn)化CSS注釋開頭

假設(shè)我們有一個又大又雜亂的CSS文件,是由幾個不同的人寫的。在這個文件中,有些注釋以/*開頭,有些以/**開頭,還有些以/*****開頭。

讓我們來寫一個Regex替換以標(biāo)準(zhǔn)化所有的單行CSS注釋,以/*開頭。

為了做到這一點,我們將擴(kuò)展表達(dá)式,只匹配以兩個或更多星號開頭的注釋。

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

這個表達(dá)式與原來的非常相似。主要區(qū)別在于開頭我們用\*{2,}替換了\*+。\*{2,}語法表示*的“兩個或多個”實例。

為了規(guī)范每個注釋的開頭,我們可以通過以下替代。

/*$2$3

讓我們在以下測試CSS文件上運行此替換。

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

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

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

結(jié)果將是與標(biāo)準(zhǔn)注釋開頭相同的文件。

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

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

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

6 – 匹配網(wǎng)址
另一個非常有用的Regex是在文本中匹配URL。

下面是一個來自Stack Overflow的URL匹配表達(dá)式的示例。

(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})——匹配域擴(kuò)展擴(kuò)展名(即“.com”或“.org”)
??? (?<path>\/[-a-zA-Z0-9@:%_\/+.~#?&=]*)?——匹配URL路徑(/posts)、查詢字符串(?limit=1)和/或文件擴(kuò)展名(.html),這些都是可選的。

6.0 – 命名捕獲組
你注意到?jīng)]有,一些捕獲組現(xiàn)在以?<name>標(biāo)識符開頭。這是命名捕獲組的語法,可以使得數(shù)據(jù)提取更加清晰。
6.1 – 真實示例 – 從Web頁面上的URL解析域名
以下是我們?nèi)绾问褂妹东@組來提取使用Python語言的網(wǎng)頁中每個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'))

腳本將打印在原始網(wǎng)頁HTML內(nèi)容中找到的每個域名。

...
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替換文本文件內(nèi)容。
7.0 – 真實示例 – 用grep匹配圖像文件
我們將定義另一個基本的Regex,這次是用于匹配圖像文件。
^.+\.(?i)(png|jpg|jpeg|gif|webp)$

??? ^——開始行。
??? .+——匹配任何字符(字母,數(shù)字,符號),除了\n(換行)之外,1+次數(shù)。
??? \.——匹配 ‘.’字符。
??? (?i)——表示下一個序列不區(qū)分大小寫。
??? (png|jpg|jpeg|gif|webp)——匹配常見的圖像文件擴(kuò)展名
??? $——結(jié)束行

以下是如何列出Downloads目錄中所有圖像文件的方法。

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

??? ls ~/Downloads——列出Downloads目錄中的文件
??? |——將輸出管道輸送到下一個命令
??? grep -E——使用正則表達(dá)式過濾輸入

7.1 – 真實例子 – 用sed進(jìn)行電子郵件替換

bash命令中正則表達(dá)式的另一個好處是在文本文件中修改電子郵件。

這可以通過使用sed命令以及前面的電子郵件Regex的修改版本完成。

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

??? sed——Unix的“流編輯器”實用程序,允許強(qiáng)大的文本文件轉(zhuǎn)換。
??? -E——使用擴(kuò)展的Regex模式匹配
??? -i——原位替換文件流
??? 's/^(.*?\s|)——將行的開頭包裝在捕獲組中
??? [^@]+@[^\s]+——電子郵件Regex的簡化版本。
??? /\1\{redacted\}/g'——用{redacted}替換每個電子郵件地址。
??? test.txt——對test.txt文件執(zhí)行操作。

我們可以在一個示例test.txt文件上運行上面的替換命令。

My email is patrick.triest@gmail.com

命令運行后,電子郵件將從test.txt文件中進(jìn)行編輯。

My email is {redacted}

警告——此命令將自動從你傳遞的任何test.txt中刪除所有電子郵件地址,因此,在運行它的時候要小心,因為此操作無法逆轉(zhuǎn)。要在終端中預(yù)覽結(jié)果,而不是替換原來的文本,只需省略-i標(biāo)志。

注意——盡管上述命令適用于大多數(shù)Linux發(fā)行版,但是macOS使用BSD實現(xiàn)是sed,它在其支持的Regex語法中受到更多的限制。要在MacOS上使用sed,并具有體面的正則表達(dá)式支持,我建議使用brew install gnu-sed安裝sed的GNU實現(xiàn),然后從命令行使用gsed而不是sed。
8 – 什么時候不使用Regex

好的,知道Regex是一個強(qiáng)大又靈活的工具了吧?!那么,有沒有應(yīng)該避免編寫Regex的時候?有!
8.0 – 語言解析

解析結(jié)構(gòu)化語言,從英語到Java到JSON,使用正則表達(dá)式都是一種真正的痛苦。

當(dāng)數(shù)據(jù)源中的邊緣情況或次要語法錯誤導(dǎo)致表達(dá)式失敗時,將導(dǎo)致最終(或即時)的災(zāi)難,出于此目的去編寫你自己的正則表達(dá)式可能會讓你心情沮喪。

強(qiáng)化的解析器幾乎可用于所有機(jī)器可讀的語言,而NLP工具可用于人類語言——我強(qiáng)烈建議你使用其中一種,而不是嘗試編寫自己的語言。
8.1 – 安全 – 輸入過濾和黑名單

使用Regex過濾用戶輸入(例如來自Web表單),以及防止黑客向應(yīng)用程序發(fā)送惡意命令(例如SQL注入),看上去似乎很誘人。

在這里使用自定義的Regex是不明智的,因為它很難覆蓋每個潛在的攻擊向量或惡意命令。例如,黑客可以使用替代字符編碼繞過編寫得不全面的輸入黑名單過濾器。

這是另一個實例,對此我強(qiáng)烈建議你使用經(jīng)過良好測試的庫和/或服務(wù),以及使用白名單而不是黑名單,以保護(hù)你的應(yīng)用程序免受惡意輸入。
8.2 – 性能密集的應(yīng)用程序

正則表達(dá)式的匹配速度可以從不是非常快到極慢的范圍變動,取決于表達(dá)式寫得怎么樣。對于大多數(shù)用例來說,這很好,特別是如果匹配的文本很短(例如電子郵件地址表單)的話。然而,對于高性能服務(wù)器應(yīng)用程序,正則表達(dá)式會成為性能瓶頸,特別是如果表達(dá)式寫得不好或被搜索的文本很長的話。
8.3 – 對于不需要Regex的地方

正則表達(dá)式是一個非常有用的工具,但這并不意味著你應(yīng)該在任何地方使用它。

如果問題有替代的解決方案,解決方案更簡單和/或不需要使用Regex,那么請不要只是為了顯擺而使用Regex。Regex很棒,但它也是最不可讀的編程工具之一,而且很容易出現(xiàn)邊緣情況和bug。

過度使用Regex會讓你的同事(以及需要工作在你的代碼上的任何人)生氣惱怒,甚至恨不得揍你一頓。
結(jié)論

我希望這是對Regex的許多用途的一個有用的介紹。

還有很多Regex的用例是我們沒有涵蓋的。例如,可以在PostgreSQL查詢中使用Regex來動態(tài)地搜索數(shù)據(jù)庫中的文本模式。

我們還漏下了許多強(qiáng)大的Regex語法特性沒有覆蓋,如lookahead,lookbehind,atomic groups,recursion和subroutines。

要提高正則表達(dá)式技能并了解有關(guān)這些功能的更多信息,我推薦以下資源。

??? 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

歡迎隨時對本教程提出任何建議、看法或批評。

總結(jié)

以上是生活随笔為你收集整理的【转】多语言的正则表达式,我们应该掌握的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲一区二区三区观看 | 欧美日韩国产第一页 | 久久夜色精品国产欧美乱 | 日韩成人一区二区 | 人人色网 | 操操操影院 | 欧美自拍视频在线观看 | 国产精品久久久久毛片 | 老熟女高潮一区二区三区 | 一区二区三区免费高清视频 | 超碰2020| 婷婷五月在线视频 | 自拍偷拍视频网站 | 美女视频黄频视频大全 | 久热综合 | japanesehdxxxx| 高清免费av | 日韩综合区 | 香蕉视频在线观看黄 | 成人涩涩网 | 国产综合网站 | 亚洲国产一区在线 | 高清视频在线播放 | 麻豆成人91精品二区三区 | 亚洲系列 | 日韩成人无码影院 | h色视频在线观看 | 黑人三级视频 | 91av入口| 丰满人妻一区二区三区大胸 | 亚洲欧洲一区二区在线观看 | 黄色链接视频 | 美女少妇毛片 | 亚洲在线视频免费观看 | 在线不卡 | 中文字幕免费在线观看视频 | 日韩一区二区三区在线观看 | 性做爰视频免费播放大全 | 国产精品久久影视 | 国内自拍第三页 | 99在线观看免费视频 | 日韩在线视频精品 | 91麻豆精品一二三区在线 | 日韩一区二区三区在线观看 | 久久综合91| 日韩亚洲欧美在线观看 | 秋霞欧洲| 热久久亚洲 | 男生和女生一起差差差很痛的视频 | 天堂在线免费视频 | 日韩在线视频免费观看 | 2017天天干 | 国产吞精囗交免费视频网站 | 色www.| 中日韩男男gay无套 人人草人人干 | 在线播放毛片 | 亚洲福利视频在线 | 久久久久久久久久国产精品 | 国产网址 | 日本啪啪片 | 中文字幕精| 五十路av | 伊人国产女 | 久热国产视频 | 欧美黄色短视频 | 国产一区二区三区色淫影院 | 国产精品久久av | 久草资源在线观看 | 亚洲在线观看免费视频 | 国产亚洲在线观看 | 日韩色在线观看 | 久久免费视频网 | 狠狠操在线视频 | 亚洲视屏在线观看 | 一区二区三区精品在线 | 伊人久久大香网 | 在线观看国产 | 成人午夜视频在线 | 黄色网页免费 | 午夜精品小视频 | 91综合国产| 黄频网站在线观看 | 91欧美一区二区 | 中文字幕av一区二区三区人妻少妇 | 99re在线视频精品 | 天天天天躁天天爱天天碰2018 | 亚洲综合欧美综合 | 亚洲图片88 | 色综合天天综合综合国产 | 36d大奶| 无码av免费精品一区二区三区 | 66精品| a片在线免费观看 | 能看的黄色网址 | 国内偷拍av| 香蕉久操| 国产精品久久伊人 | 黄网av| 久久久96|