dnrops.gitlink.net/posts/swift/swift_regular_exressions.html

491 lines
56 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="coal" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Swift regular exressions - Andrew&#x27;s Blog</title>
<!-- Custom HTML head -->
<meta name="description" content="Andrew Ryan&#x27;s Blog">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../../favicon.svg">
<link rel="shortcut icon" href="../../favicon.png">
<link rel="stylesheet" href="../../css/variables.css">
<link rel="stylesheet" href="../../css/general.css">
<link rel="stylesheet" href="../../css/chrome.css">
<!-- Fonts -->
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../../highlight.css">
<link rel="stylesheet" href="../../tomorrow-night.css">
<link rel="stylesheet" href="../../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../../src/style/custom.css">
<!-- MathJax -->
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</head>
<body class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "../../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "coal" : "coal";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('coal')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item affix "><a href="../../index.html">Andrew's Blog</a></li><li class="chapter-item "><a href="../../posts/linux/linux.html"><strong aria-hidden="true">1.</strong> linux</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/linux/install_linux.html"><strong aria-hidden="true">1.1.</strong> install linux</a></li><li class="chapter-item "><a href="../../posts/linux/bash_profile.html"><strong aria-hidden="true">1.2.</strong> bash profile</a></li><li class="chapter-item "><a href="../../posts/linux/command_list.html"><strong aria-hidden="true">1.3.</strong> command list</a></li><li class="chapter-item "><a href="../../posts/linux/git_guide.html"><strong aria-hidden="true">1.4.</strong> git guide</a></li><li class="chapter-item "><a href="../../posts/linux/tar.html"><strong aria-hidden="true">1.5.</strong> tar</a></li><li class="chapter-item "><a href="../../posts/linux/run_x86_elf_in_x64_setup.html"><strong aria-hidden="true">1.6.</strong> run x86 elf in x64 setup</a></li></ol></li><li class="chapter-item "><a href="../../posts/mac/mac.html"><strong aria-hidden="true">2.</strong> mac</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/mac/macos_profiles.html"><strong aria-hidden="true">2.1.</strong> macos profiles</a></li></ol></li><li class="chapter-item expanded "><a href="../../posts/swift/swift.html"><strong aria-hidden="true">3.</strong> swift</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/swift/learn_swift.html"><strong aria-hidden="true">3.1.</strong> learn swift basics</a></li><li class="chapter-item "><a href="../../posts/swift/swift_extensions.html"><strong aria-hidden="true">3.2.</strong> Swift extensions</a></li><li class="chapter-item "><a href="../../posts/swift/swiftui_extension.html"><strong aria-hidden="true">3.3.</strong> SwiftUI extensions</a></li><li class="chapter-item "><a href="../../posts/swift/install_swift.html"><strong aria-hidden="true">3.4.</strong> install swift</a></li><li class="chapter-item "><a href="../../posts/swift/task_planner.html"><strong aria-hidden="true">3.5.</strong> implment task panner app with SwiftUI</a></li><li class="chapter-item "><a href="../../posts/swift/swift_cheat_sheet.html"><strong aria-hidden="true">3.6.</strong> Swift Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/swift/yinci_url.html"><strong aria-hidden="true">3.7.</strong> Personal privacy protocol</a></li><li class="chapter-item expanded "><a href="../../posts/swift/swift_regular_exressions.html" class="active"><strong aria-hidden="true">3.8.</strong> Swift regular exressions</a></li><li class="chapter-item "><a href="../../posts/ios/how_to_create_beautiful_ios_charts_in_swift.html"><strong aria-hidden="true">3.9.</strong> How to Create Beautiful iOS Charts in鑱絊wift</a></li><li class="chapter-item "><a href="../../posts/swift/swiftui_source_code.html"><strong aria-hidden="true">3.10.</strong> SwiftUI source code</a></li><li class="chapter-item "><a href="../../posts/swift/use_swift_fetch_iciba_api.html"><strong aria-hidden="true">3.11.</strong> use swift fetch iciba API</a></li></ol></li><li class="chapter-item "><a href="../../posts/ios/ios.html"><strong aria-hidden="true">4.</strong> ios</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ios/cocaposd_setup_and_install_for_ios_project.html"><strong aria-hidden="true">4.1.</strong> cocaposd setup and install for ios project</a></li><li class="chapter-item "><a href="../../posts/ios/swiftui_show_gif_image.html"><strong aria-hidden="true">4.2.</strong> SwiftUI show gif image</a></li><li class="chapter-item "><a href="../../posts/ios/implement_task_planner_app.html"><strong aria-hidden="true">4.3.</strong> implement Task planner App</a></li></ol></li><li class="chapter-item "><a href="../../posts/objective_c/objective_c.html"><strong aria-hidden="true">5.</strong> objective_c</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/objective_c/objective_c_cheat_sheet.html"><strong aria-hidden="true">5.1.</strong> Objective-C Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/objective_c/objective_c_for_absolute_beginners_read_note.html"><strong aria-hidden="true">5.2.</strong> Objective-C Note</a></li></ol></li><li class="chapter-item "><a href="../../posts/dart/dart.html"><strong aria-hidden="true">6.</strong> dart</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/dart/flutter.html"><strong aria-hidden="true">6.1.</strong> Flutter Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/dart/dart_cheat_sheet.html"><strong aria-hidden="true">6.2.</strong> Dart Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/flutter/flutter_dev_test.html"><strong aria-hidden="true">6.3.</strong> Flutter dev test</a></li></ol></li><li class="chapter-item "><a href="../../posts/rust/rust.html"><strong aria-hidden="true">7.</strong> rust</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/rust/offline_use_rust.html"><strong aria-hidden="true">7.1.</strong> Offline use rust</a></li><li class="chapter-item "><a href="../../posts/rust/rust_grammer.html"><strong aria-hidden="true">7.2.</strong> rust grammar</a></li><li class="chapter-item "><a href="../../posts/rust/pase_string_and_decimal_conversion.html"><strong aria-hidden="true">7.3.</strong> pase string and decimal conversion</a></li><li class="chapter-item "><a href="../../posts/rust/parse_types.html"><strong aria-hidden="true">7.4.</strong> rust types</a></li><li class="chapter-item "><a href="../../posts/rust/rust_life_cycle.html"><strong aria-hidden="true">7.5.</strong> Rust life cycle</a></li><li class="chapter-item "><a href="../../posts/rust/rust_generic.html"><strong aria-hidden="true">7.6.</strong> rust generics</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implment_matrix.html"><strong aria-hidden="true">7.7.</strong> Rust implement matrix</a></li><li class="chapter-item "><a href="../../posts/rust/rust_sort.html"><strong aria-hidden="true">7.8.</strong> Rust implement sort algorithms</a></li><li class="chapter-item "><a href="../../posts/rust/implement_aes_encryption.html"><strong aria-hidden="true">7.9.</strong> Rust implement AEC encryption and decryption</a></li><li class="chapter-item "><a href="../../posts/rust/implement_trie_data_structure.html"><strong aria-hidden="true">7.10.</strong> implement trie data structure</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implement_tree.html"><strong aria-hidden="true">7.11.</strong> implement tree data_structure</a></li><li class="chapter-item "><a href="../../posts/rust/list_dir.html"><strong aria-hidden="true">7.12.</strong> list dir</a></li><li class="chapter-item "><a href="../../posts/rust/fast_way_to_implment_object_trait.html"><strong aria-hidden="true">7.13.</strong> fast way to implment object trait</a></li><li class="chapter-item "><a href="../../posts/rust/compress_rust_binary_size.html"><strong aria-hidden="true">7.14.</strong> compress rust binary size</a></li><li class="chapter-item "><a href="../../posts/rust/implment_file_upload_backend.html"><strong aria-hidden="true">7.15.</strong> impliment file upload</a></li><li class="chapter-item "><a href="../../posts/rust/this_is_add_post_cli_implementation_in_rust.html"><strong aria-hidden="true">7.16.</strong> this is add_post cli implementation in rust</a></li><li class="chapter-item "><a href="../../posts/rust/use_rust_implment_a_copyclipbord_cli.html"><strong aria-hidden="true">7.17.</strong> Use rust implment a copyclipbord CLI</a></li><li class="chapter-item "><a href="../../posts/rust/sqlite_database_add_delete_update_show_in_rust.html"><strong aria-hidden="true">7.18.</strong> sqlite database add delete update show in rust</a></li><li class="chapter-item "><a href="../../posts/rust/implementing_tokio_joinhandle_for_wasm.html"><strong aria-hidden="true">7.19.</strong> Implementing tokio JoinHandle for wasm</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implement_a_crate_for_encode_and_decode_brainfuck_and_ook.html"><strong aria-hidden="true">7.20.</strong> rust implement a crate for encode and decode brainfuck and ook</a></li><li class="chapter-item "><a href="../../posts/rust/slint_builtin_elements.html"><strong aria-hidden="true">7.21.</strong> Slint Builtin Elements</a></li><li class="chapter-item "><a href="../../posts/rust/corporate_network_install_rust_on_windows.html"><strong aria-hidden="true">7.22.</strong> Corporate network install Rust on windows</a></li><li class="chapter-item "><a href="../../posts/rust/rust_binary_file_how_to_judge_static_link_or_dynamic_link_in_macos.html"><strong aria-hidden="true">7.23.</strong> rust binary file how to judge static link or dynamic link in Macos</a></li><li class="chapter-item "><a href="../../posts/rust/rust_binary_include_dir_and_get_contents.html"><strong aria-hidden="true">7.24.</strong> rust binary include dir and get contents</a></li><li class="chapter-item "><a href="../../posts/rust/rust_logger_non-block.html"><strong aria-hidden="true">7.25.</strong> rust logger non-block</a></li><li class="chapter-item "><a href="../../posts/rust/rust_connect_sql_server_database.html"><strong aria-hidden="true">7.26.</strong> rust connect sql server database</a></li><li class="chapter-item "><a href="../../posts/rust/rust_websocket_implment.html"><strong aria-hidden="true">7.27.</strong> rust websocket implment</a></li></ol></li><li class="chapter-item "><a href="../../posts/java/java.html"><strong aria-hidden="true">8.</strong> java</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/java/java_grammar.html"><strong aria-hidden="true">8.1.</strong> java grammar and codewar</a></li><li class="chapter-item "><a href="../../posts/java/run_jar.html"><strong aria-hidden="true">8.2.</strong> java run .jar</a></li><li class="chapter-item "><a href="../../posts/java/java_pomxml_add_defaultgoal_to_build.html"><strong aria-hidden="true">8.3.</strong> Java pomxml add defaultGoal to build</a></li><li class="chapter-item "><a href="../../posts/java/java_set_mvn_mirror.html"><strong aria-hidden="true">8.4.</strong> Java set mvn mirror</a></li></ol></li><li class="chapter-item "><a href="../../posts/python/python.html"><strong aria-hidden="true">9.</strong> python</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/python/convert_pesn.html"><strong aria-hidden="true">9.1.</strong> convert pesn</a></li><li class="chapter-item "><a href="../../posts/python/find_remove_dir.html"><strong aria-hidden="true">9.2.</strong> find and remove dir</a></li><li class="chapter-item "><a href="../../posts/python/timing_message.html"><strong aria-hidden="true">9.3.</strong> wechat send message</a></li><li class="chapter-item "><a href="../../posts/python/use_python_openpyxl_package_read_and_edit_excel_files.html"><strong aria-hidden="true">9.4.</strong> Use python openpyxl package read and edit excel files</a></li></ol></li><li class="chapter-item "><a href="../../posts/go/go.html"><strong aria-hidden="true">10.</strong> go</a></li><li class="chapter-item "><a href="../../posts/js/js.html"><strong aria-hidden="true">11.</strong> js</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/js/js_tutorial.html"><strong aria-hidden="true">11.1.</strong> js tutorial</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_map.html"><strong aria-hidden="true">11.2.</strong> ja map</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_math.html"><strong aria-hidden="true">11.3.</strong> js math</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_object.html"><strong aria-hidden="true">11.4.</strong> js object</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_set.html"><strong aria-hidden="true">11.5.</strong> js set</a></li><li class="chapter-item "><a href="../../posts/js/single_thread_and_asynchronous.html"><strong aria-hidden="true">11.6.</strong> single thread and asynchronous</a></li><li class="chapter-item "><a href="../../posts/js/this.html"><strong aria-hidden="true">11.7.</strong> js this</a></li><li class="chapter-item "><a href="../../posts/js/js_implment_aes.html"><strong aria-hidden="true">11.8.</strong> js implment aes</a></li><li class="chapter-item "><a href="../../posts/js/getting_started_with_ajax.html"><strong aria-hidden="true">11.9.</strong> getting started with ajax</a></li><li class="chapter-item "><a href="../../posts/js/BinarySearchTree.html"><strong aria-hidden="true">11.10.</strong> binary search tree</a></li><li class="chapter-item "><a href="../../posts/js/goole_zx.html"><strong aria-hidden="true">11.11.</strong> goole zx</a></li><li class="chapter-item "><a href="../../posts/js/es6.html"><strong aria-hidden="true">11.12.</strong> es6</a></li></ol></li><li class="chapter-item "><a href="../../posts/ruby/ruby.html"><strong aria-hidden="true">12.</strong> ruby</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ruby/rails_setup_env.html"><strong aria-hidden="true">12.1.</strong> ruby on rails setup environment</a></li><li class="chapter-item "><a href="../../posts/ruby/learn_ruby.html"><strong aria-hidden="true">12.2.</strong> learn ruby</a></li><li class="chapter-item "><a href="../../posts/ruby/ruby_note.html"><strong aria-hidden="true">12.3.</strong> Ruby Note</a></li><li class="chapter-item "><a href="../../posts/ruby/setup_ruby_for_ctf.html"><strong aria-hidden="true">12.4.</strong> Setup ruby for CTF</a></li></ol></li><li class="chapter-item "><a href="../../posts/react/react.html"><strong aria-hidden="true">13.</strong> react</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/react/react_life_cycle.html"><strong aria-hidden="true">13.1.</strong> react life cycle</a></li><li class="chapter-item "><a href="../../posts/react/react_router.html"><strong aria-hidden="true">13.2.</strong> react router</a></li><li class="chapter-item "><a href="../../posts/react/react_this.html"><strong aria-hidden="true">13.3.</strong> react this</a></li><li class="chapter-item "><a href="../../posts/react/react_interviw.html"><strong aria-hidden="true">13.4.</strong> react interview</a></li><li class="chapter-item "><a href="../../posts/react/important_react_interview.html"><strong aria-hidden="true">13.5.</strong> important react interview</a></li><li class="chapter-item "><a href="../../posts/react/react_quick_reference.html"><strong aria-hidden="true">13.6.</strong> react quick reference</a></li><li class="chapter-item "><a href="../../posts/react/redux_quick_reference.html"><strong aria-hidden="true">13.7.</strong> redux quick reference</a></li></ol></li><li class="chapter-item "><a href="../../posts/vue/vue.html"><strong aria-hidden="true">14.</strong> vue</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/vue/vue_ajax.html"><strong aria-hidden="true">14.1.</strong> vue ajax</a></li></ol></li><li class="chapter-item "><a href="../../posts/angular/angular.html"><strong aria-hidden="true">15.</strong> angular</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/angular/controller_communication.html"><strong aria-hidden="true">15.1.</strong> controller communication</a></li><li class="chapter-item "><a href="../../posts/angular/creating_custom_directives.html"><strong aria-hidden="true">15.2.</strong> creating custom directives</a></li><li class="chapter-item "><a href="../../posts/angular/directive_notes.html"><strong aria-hidden="true">15.3.</strong> directive notes</a></li><li class="chapter-item "><a href="../../posts/angular/directive_communication.html"><strong aria-hidden="true">15.4.</strong> directive communication</a></li><li class="chapter-item "><a href="../../posts/angular/post_params.html"><strong aria-hidden="true">15.5.</strong> post params</a></li><li class="chapter-item "><a href="../../posts/angular/read_json_angular.html"><strong aria-hidden="true">15.6.</strong> read json angular</a></li><li class="chapter-item "><a href="../../posts/angular/same_route_reload.html"><strong aria-hidden="true">15.7.</strong> same route reload</a></li></ol></li><li class="chapter-item "><a href="../../posts/css/css.html"><strong aria-hidden="true">16.</strong> css</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/css/use_css_media.html"><strong aria-hidden="true">16.1.</strong> use css media</a></li></ol></li><li class="chapter-item "><a href="../../posts/php/php.html"><strong aria-hidden="true">17.</strong> php</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/php/for_php_string_implment_some_extemtion_functions.html"><strong aria-hidden="true">17.1.</strong> for php string implment some extemtion functions</a></li><li class="chapter-item "><a href="../../posts/php/php_cheatsheet.html"><strong aria-hidden="true">17.2.</strong> PHP cheatsheet</a></li></ol></li><li class="chapter-item "><a href="../../posts/leetcode/leetcode.html"><strong aria-hidden="true">18.</strong> leetcode</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/leetcode/rust_leetcode.html"><strong aria-hidden="true">18.1.</strong> rust leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_codewar.html"><strong aria-hidden="true">18.2.</strong> rust codewar</a></li><li class="chapter-item "><a href="../../posts/leetcode/swift_codewar.html"><strong aria-hidden="true">18.3.</strong> swift codewar</a></li><li class="chapter-item "><a href="../../posts/leetcode/js_leetcode.html"><strong aria-hidden="true">18.4.</strong> js leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/java_leetcode.html"><strong aria-hidden="true">18.5.</strong> java leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_huawei.html"><strong aria-hidden="true">18.6.</strong> huawei test</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_utils.html"><strong aria-hidden="true">18.7.</strong> rust common functions</a></li><li class="chapter-item "><a href="../../posts/leetcode/olympiad_training.html"><strong aria-hidden="true">18.8.</strong> Computer olympiad training</a></li></ol></li><li class="chapter-item "><a href="../../posts/ctf/CTF.html"><strong aria-hidden="true">19.</strong> ctf</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ctf/CTF_Note.html"><strong aria-hidden="true">19.1.</strong> CTF Note</a></li><li class="chapter-item "><a href="../../posts/ctf/0.1_Web.html"><strong aria-hidden="true">19.2.</strong> Web</a></li><li class="chapter-item "><a href="../../posts/ctf/4.1_Misc.html"><strong aria-hidden="true">19.3.</strong> Misc</a></li><li class="chapter-item "><a href="../../posts/ctf/3.2_PWN_note.html"><strong aria-hidden="true">19.4.</strong> PWN</a></li><li class="chapter-item "><a href="../../posts/ctf/3.1_Crypto.html"><strong aria-hidden="true">19.5.</strong> Crypto</a></li><li class="chapter-item "><a href="../../posts/ctf/3.4_RSA_note.html"><strong aria-hidden="true">19.6.</strong> Rsa attack</a></li><li class="chapter-item "><a href="../../posts/ctf/3.5_Base64.html"><strong aria-hidden="true">19.7.</strong> Base64</a></li><li class="chapter-item "><a href="../../posts/ctf/0.0_SQL Injection Cheatsheet.html"><strong aria-hidden="true">19.8.</strong> SQL Injection Cheatsheet</a></li><li class="chapter-item "><a href="../../posts/ctf/1.1_SQL_injection.html"><strong aria-hidden="true">19.9.</strong> SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.2_SQL_injection_UNION_attacks.html"><strong aria-hidden="true">19.10.</strong> SQL Injection UNION attacks</a></li><li class="chapter-item "><a href="../../posts/ctf/1.3_Blind SQL injection.html"><strong aria-hidden="true">19.11.</strong> Blind SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.4_Code Injection.html"><strong aria-hidden="true">19.12.</strong> Code Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.5_SSRF.html"><strong aria-hidden="true">19.13.</strong> SSRF</a></li><li class="chapter-item "><a href="../../posts/ctf/1.6_OS command injection.html"><strong aria-hidden="true">19.14.</strong> OS command injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.7_Local file inclusion.html"><strong aria-hidden="true">19.15.</strong> Local file inclusion</a></li><li class="chapter-item "><a href="../../posts/ctf/1.8_Remote file inclusion.html"><strong aria-hidden="true">19.16.</strong> Remote file inclusion</a></li><li class="chapter-item "><a href="../../posts/ctf/1.9_CSRFm.html"><strong aria-hidden="true">19.17.</strong> CSRF</a></li><li class="chapter-item "><a href="../../posts/ctf/1.10_NoSQL injection.html"><strong aria-hidden="true">19.18.</strong> NoSQL injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.11_JSON injection.html"><strong aria-hidden="true">19.19.</strong> JSON injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.12_CTF_Web_SQL_Note.html"><strong aria-hidden="true">19.20.</strong> CTF Web SQL Note</a></li><li class="chapter-item "><a href="../../posts/ctf/2.1_XXE.html"><strong aria-hidden="true">19.21.</strong> XXE</a></li><li class="chapter-item "><a href="../../posts/ctf/2.2_XSS.html"><strong aria-hidden="true">19.22.</strong> XSS</a></li><li class="chapter-item "><a href="../../posts/ctf/2.3_Upload File.html"><strong aria-hidden="true">19.23.</strong> Upload File</a></li><li class="chapter-item "><a href="../../posts/ctf/2.4_serialize_unserialize.html"><strong aria-hidden="true">19.24.</strong> serialize unserialize</a></li><li class="chapter-item "><a href="../../posts/ctf/2.5_Race condition.html"><strong aria-hidden="true">19.25.</strong> Race condition</a></li><li class="chapter-item "><a href="../../posts/ctf/3.2_PWN_note.html"><strong aria-hidden="true">19.26.</strong> PWN_note</a></li><li class="chapter-item "><a href="../../posts/ctf/3.3_pwn HCTF2016 brop.html"><strong aria-hidden="true">19.27.</strong> pwn HCTF2016 brop</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_patch_defense_skill.html"><strong aria-hidden="true">19.28.</strong> PWN Patch defense skill</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_stack_overflow.html"><strong aria-hidden="true">19.29.</strong> PWN stack overflow</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_heap_overflow.html"><strong aria-hidden="true">19.30.</strong> PWN heap overflow</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_format_string_vulnerability.html"><strong aria-hidden="true">19.31.</strong> PWN Format String Vulnerability</a></li><li class="chapter-item "><a href="../../posts/ctf/kali_linux_tutorials.html"><strong aria-hidden="true">19.32.</strong> Kali linux tutorials</a></li><li class="chapter-item "><a href="../../posts/ctf/google_dorks_2023_lists.html"><strong aria-hidden="true">19.33.</strong> Google Dorks 2023 Lists</a></li><li class="chapter-item "><a href="../../posts/ctf/dvwa_writeup.html"><strong aria-hidden="true">19.34.</strong> DVWA WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/bwapp_writeup.html"><strong aria-hidden="true">19.35.</strong> bWAPP WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/sqlilabs_writeup.html"><strong aria-hidden="true">19.36.</strong> sqlilabs WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/ctf_train_at_hangzhou.html"><strong aria-hidden="true">19.37.</strong> ctf train at hangzhou</a></li><li class="chapter-item "><a href="../../posts/ctf/ctf_common_mindmap_list.html"><strong aria-hidden="true">19.38.</strong> ctf common mindmap list</a></li><li class="chapter-item "><a href="../../posts/ctf/error_based_sql_injection.html"><strong aria-hidden="true">19.39.</strong> Error Based SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/urlfinder_tutorial.html"><strong aria-hidden="true">19.40.</strong> URLFinder Tutorial</a></li><li class="chapter-item "><a href="../../posts/ctf/observer_ward_tutorial.html"><strong aria-hidden="true">19.41.</strong> observer_ward Tutorial</a></li><li class="chapter-item "><a href="../../posts/ctf/mysql_udf_.html"><strong aria-hidden="true">19.42.</strong> MySQL UDF 提权</a></li><li class="chapter-item "><a href="../../posts/ctf/nuclei__tutorial.html"><strong aria-hidden="true">19.43.</strong> Nuclei Tutorial</a></li><li class="chapter-item "><a href="../../posts/ctf/2024_ctf_solution_thinking.html"><strong aria-hidden="true">19.44.</strong> 2024 ctf solution thinking</a></li><li class="chapter-item "><a href="../../posts/ctf/man_che_si_te_bian_ma.html"><strong aria-hidden="true">19.45.</strong> 曼彻斯特编码</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Andrew&#x27;s Blog</h1>
<div class="right-buttons">
<a href="https://gitlink.org.cn/dnrops/dnrops.gitlink.net.git" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="swift-regular-exressions"><a class="header" href="#swift-regular-exressions">Swift regular exressions</a></h1>
<p style="display:flex;
align-items: center;
justify-content: end;
">Pub Date: 2023-07-25</p>
Swift 5.7 中引入了正则表达式的语法支持,整理一下相关的一些话题、方法和示例,以备今后自己能够速查。
## 总览
Swift 正则由标准库中的 `Regex` 类型驱动,需要 iOS 16.0 或 macOS 13.0,早期的 deploy 版本无法使用。
构建一个正则表达式的方式,分为传统的正则字面量构建,以及通过 Regex Builder DSL 的更加易读的方式。后者可以内嵌使用前者,以及其他一些已有的 parser在可读性和功能上要强力很多。实践中推荐**结合使用字面量和 Builder API 在简洁和易读之间获取平衡**。
## 常见字面量
和其他各语言正则表达式的字面量没有显著不同。
直接将字面量包裹在 `/.../` 中使用Swift 将把类似的声明转换为 `Regex` 类型的实例:
```swift
let bitcoinAddress_v1 = /([13][a-km-zA-HJ-NP-Z0-9]{26,33})/
```
### 字符集
| 表达式 | 说明 | 示例 |
| ---------- | ------------------------------------- | --------------------------------------------- |
| `[aeiou]` | 匹配指定字符集 | On**e**Vs␣D**e**n␣**i**s␣**a**␣bl**o**g. |
| `[^aeiou]` | 排除字符集 | **On**e**Vs␣D**e**n␣**i**s␣**a**␣bl**o**g.** |
| `[A-Z]` | 匹配字符范围 | **O**ne**V**s␣**D**en␣is␣a␣blog. |
| `.` | 除换行符以外的任意字符。等效于 `[^\n\r]` | **OneVs␣Den␣is␣a␣blog.** |
| `\s` | 匹配空格字符 (包括 tab 和换行) | OneVs**␣**Den**␣**is**␣**a**␣**blog. |
| `\S` | 匹配非空格字符 | **OneVs**␣**Den**␣**is**␣**a**␣**blog.** |
| `[\s\S]` | 匹配空格和非空格,也即任意字符。等效于 `[^]` | **OneVs␣Den␣is␣a␣blog.** |
| `\w` | 匹配字母数字下划线等低位 ASCII。等效于 `[A-Za-z0-9_]` | **OneV****s**␣**Den**␣**is**␣**a**␣**blog**. |
| `\W` | 等效于 `[^A-Za-z0-9_]` |   |
| `\d` | 匹配数字,等效于 `[0-9]` | +(**81**)**021**-**1234**-**5678** |
| `\D` | 非数字,等效于 `[^0-9]` | **+(** 81 **)** 021**-**1234**-**5678 |
### 数量
| 表达式 | 说明 | 示例 | 结果 |
| -------- | ------------------ | ------------- | --------------------------------------- |
| `+` | 匹配一个或多个 | `b\w+` | b **be** **bee** **beer** **beers** |
| `*` | 匹配零个或多个 | `b\w*` | **b** **be** **bee** **beer** **beers** |
| `{2,3}` | 匹配若干个 | `b\w{2,3}` | b be **bee** **beer** **beer**s |
| `?` | 匹配零个或一个 | `colou?r` | **color** **colour** |
| 数量 + `?` | 使前置数量进行惰性匹配 (尽可能少) | `b\w+?` | b **be** **be**e **be**er **be**ers |
| `\|` | 逻辑或,择一匹配 | `b(a\|e\|i)d` | **bad** bud bod **bed** **bid** |
### 锚点
| 表达式 | 说明 | 示例 | 结果 |
| ---- | --------------------- | ------ | ----------------------------------- |
| `^` | 匹配字符串开头 | `^\w+` | **she** sells seashells |
| `$` | 匹配字符串结尾 | `\w+$` | she sells **seashells** |
| `\b` | 匹配 `\w` 和非 `\w` 的边缘位置 | `s\b` | she sell**s** seashell**s** |
| `\B` | 匹配非边缘位置 | `s\B` | **s**he **s**ells **s**ea**s**hells |
### 捕获组
| 表达式 | 说明 | 示例 |
| ---------------- | ------------------------- | ------------------------- |
| `(OneV)+` | 捕获括号内的匹配,使其成组并出现在匹配结果中 | **OneV**s Den is a blog. |
| `(?<name>OneV)+` | 命名捕获匹配,在结果中可使用名字对匹配结果进行引用 |   |
| `(?:OneV)+` | 成组但不进行捕获,允许使用数量但不关心和捕获结果 |   |
### Lookahead
| 表达式 | 说明 | 示例 |
| ---------- | ----------------------------------------- | ----------------------- |
| `\d(?=px)` | `?=` - Positive lookahead。预先检查符合时再进行主体匹配 | 1pt **2**px 3em **4**px |
| `\d(?!px)` | `?!` - Negative lookahead。预先检查不符合时进行主体匹配 | **1**pt 2px **3**em 4px
## Builder DSL
字面量表达式虽然简洁,但是对应复杂情境会难以理解,也不便于修改。使用 [`RegexBuilder` 框架](https://developer.apple.com/documentation/regexbuilder)提供的 DSL 来描述正则表达式是更具有表达性的方法。
比如,
```swift
let bitcoinAddress_v1 = /([13][a-km-zA-HJ-NP-Z0-9]{26,33})/
```
等效于:
```swift
<p>import RegexBuilder
let bitcoinAddress_v1 = Regex {
Capture {
One(.anyOf(“13”))
Repeat(26…33) {
CharacterClass(
(“a”…“k”),
(“m”…“z”),
(“A”…“H”),
(“J”…“N”),
(“P”…“Z”),
(“0”…“9”)
)
}
}
}</p>
<pre><code>`Regex.init(_:)` 接受一个 result builder 形式的闭包,你可以往闭包中塞入多个 `RegexComponent` 来构建完整的正则表达式。注意 `Regex` 类型本身也满足 `RegexComponent` 协议,所以你也可以直接把字面量传递给 `Regex` 初始化方法。
字面量所提供的特性,在 Regex Builder 中都有对应。除此之外Swift Regex Builder 框架还提供了更易读的强类型描述。一些常见的对应 `RegexComponent` 如下:
### 字符集
字符集相关的 `RegexComponent` 基本被定义在 [`CharacterClass`](https://developer.apple.com/documentation/regexbuilder/characterclass) 中。
| 字面量表达式 | 等效的 `RegexComponent` |
| ---------- | ------------------------------------------------------- |
| `[aeiou]` | `.anyOf("aeiou")`。为了可读性,可以考虑加上量词 `One(.anyOf("aeiou"))` |
| `[^aeiou]` | `CharacterClass.anyOf("aeiou").inverted` |
| `[A-Z]` | `("A"..."Z")` |
| `.` | `.any` |
| `\s` | `.whitespace` |
| `\S` | `.whitespace.inverted` |
| `[\s\S]` | `CharacterClass(.whitespace, .whitespace.inverted)` |
| `\w` | `.word` |
| `\W` | `.word.inverted` |
| `\d` | `.digit` |
| `\D` | `.digit.inverted` |
### 数量
| 表达式 | 说明 | 示例 | 结果 |
| -------- | ------------------ | ------------- | --------------------------------------- |
| `+` | 匹配一个或多个 | `b\w+` | b **be** **bee** **beer** **beers** |
| `*` | 匹配零个或多个 | `b\w*` | **b** **be** **bee** **beer** **beers** |
| `{2,3}` | 匹配若干个 | `b\w{2,3}` | b be **bee** **beer** **beer**s |
| `?` | 匹配零个或一个 | `colou?r` | **color** **colour** |
| 数量 + `?` | 使前置数量进行惰性匹配 (尽可能少) | `b\w+?` | b **be** **be**e **be**er **be**ers |
| `\|` | 逻辑或,择一匹配 | `b(a\|e\|i)d` | **bad** bud bod **bed** **bid**
### 锚点
| 表达式 | 说明 | 示例 | 结果 |
| ---- | --------------------- | ------ | ----------------------------------- |
| `^` | 匹配字符串开头 | `^\w+` | **she** sells seashells |
| `$` | 匹配字符串结尾 | `\w+$` | she sells **seashells** |
| `\b` | 匹配 `\w` 和非 `\w` 的边缘位置 | `s\b` | she sell**s** seashell**s** |
| `\B` | 匹配非边缘位置 | `s\B` | **s**he **s**ells **s**ea**s**hells
此外:
- 对于多行匹配模式的情况 (如带有 `m` 的 `/^abc/m`),此时 `^` 和 `$` 等效为 `.startOfLine``.endOfLine` 等。
- 对于 Unicode 支持,常用的还有 `.textSegmentBoundary (\y)` 等。
### 捕获
| 字面量表达式 | 等效的 `RegexComponent` |
| ---------------- | ----------------------------------------------------------------------------------- |
| `(OneV)+` | `OneOrMore { Capture { "OneV" } }` |
| `(?&lt;name&gt;OneV)+` | `let name = Reference(Substring.self)` `OneOrMore { Capture(as: name) { "OneV" } }` |
| `(?:OneV)+` | `OneOrMore { "OneV" }` |
Regex Builder 支持在 `Capture` 的过程中同时进行 mapping把结果转换为其他形式的字符串甚至是其他类型的强类型值
```swift
Regex {
TryCapture(as: kind) {
OneOrMore(.word)
} transform: {
Transaction.Kind($0)
} // 得到一个强类型 `Kind` 值
}
</code></pre>
<p>如果转换可能会失败并返回 <code>nil</code>,使用 <code>TryCapture</code>:失败时跳过匹配;如果转换一定会成功,使用普通的 <code>Capture</code></p>
<h3 id="lookahead"><a class="header" href="#lookahead">Lookahead</a></h3>
<div class="table-wrapper"><table><thead><tr><th>字面量表达式</th><th>等效的 <code>RegexComponent</code></th></tr></thead><tbody>
<tr><td><code>\d(?=px)</code></td><td><code>Regex { .digit ↵ Lookahead { "px" } }</code></td></tr>
<tr><td><code>\d(?!px)</code></td><td><code>Regex { .digit ↵ NegativeLookahead { "px" } }</code></td></tr>
</tbody></table>
</div>
<h2 id="常用-parser"><a class="header" href="#常用-parser">常用 Parser</a></h2>
<p>相对于字面量,使用 Regex Builder 的最大优势,在于可以嵌套使用已经存在的 Parser 进行匹配。凡是满足 <code>RegexComponent</code> 的值,都可以放到 <code>Regex</code> 表达式中。Foundation 中,部分 <code>ParseStrategy</code> 满足 <code>RegexComponent</code> 并提供相应方法来创建 <code>Regex</code> 中可用的 parser。iOS 16 中,默认可用 Parser 有:</p>
<div class="table-wrapper"><table><thead><tr><th>所属 Parser 类型</th><th>方法签名</th><th>可解析示例</th></tr></thead><tbody>
<tr><td><code>Date.ParseStrategy</code></td><td><code>date(_:locale:timeZone:calendar:)</code></td><td>Oct 21, 2015, 10/21/2015, etc</td></tr>
<tr><td><code>Date.ParseStrategy</code></td><td><code>date(format:locale:timeZone:calendar:twoDigitStartDate:)</code></td><td>05_04_22</td></tr>
<tr><td><code>Date.ParseStrategy</code></td><td><code>dateTime(date:time:locale:timeZone:calendar:)</code></td><td>10/17/2020, 9:54:29 PM</td></tr>
<tr><td><code>Date.ISO8601FormatStyle</code></td><td><code>iso8601(timeZone:...)</code></td><td>2021-06-21T211015</td></tr>
<tr><td><code>Date.ISO8601FormatStyle</code></td><td><code>iso8601Date(timeZone:dateSeparator:)</code></td><td>2015-11-14</td></tr>
<tr><td><code>Date.ISO8601FormatStyle</code></td><td><code>iso8601WithTimeZone(...)</code></td><td>2021-06-21T21:10:15+0800</td></tr>
<tr><td><code>Decimal.FormatStyle.Currency</code></td><td><code>localizedCurrency(code:locale:)</code></td><td>$52,249.98 -&gt; <code>Decimal</code></td></tr>
<tr><td><code>Decimal.FormatStyle</code></td><td><code>localizedDecimal(locale:)</code></td><td>1.234, 1E5 -&gt; <code>Decimal</code></td></tr>
<tr><td><code>FloatingPointFormatStyle&lt;Double&gt;</code></td><td><code>localizedDouble(locale:)</code></td><td>1.234, 1E5 -&gt; -&gt; <code>Double</code></td></tr>
<tr><td><code>FlatingPointFormatStyle&lt;Double&gt;.Percent</code></td><td><code>localizedDoublePercentage(locale:)</code></td><td>15.4%, <code>-200%</code> -&gt; <code>Double</code></td></tr>
<tr><td><code>IntegerFormatStyle&lt;Int&gt;</code></td><td><code>localizedInteger(locale:)</code></td><td>199, 1.234 -&gt; <code>Int</code></td></tr>
<tr><td><code>IntegerFormatStyle&lt;Int&gt;.Currency</code></td><td><code>localizedIntegerCurrency(code:locale:)</code></td><td>$52,249.98 -&gt; <code>Int</code></td></tr>
<tr><td><code>IntegerFormatStyle&lt;Int&gt;.Percent</code></td><td><code>localizedIntegerPercentage(locale:)</code></td><td>15.4%, -200% -&gt; <code>Int</code></td></tr>
</tbody></table>
</div>
<blockquote>
<p>关于 Foundation 中 <code>ParseStrategy</code> 的相关内容,可以参看肘子兄的<a href="https://www.fatbobman.com/posts/newFormatter/">这篇博客</a>,以及 WWDC 21 中<a href="https://developer.apple.com/videos/play/wwdc2021/10109/">相关的视频</a></p>
</blockquote>
<h3 id="自定义-parser-和-customconsumingregexcomponent"><a class="header" href="#自定义-parser-和-customconsumingregexcomponent">自定义 Parser 和 <code>CustomConsumingRegexComponent</code></a></h3>
<p>对于自己实现的或是第三方提供的 Parser可以通过满足 <code>CustomConsumingRegexComponent</code> 来让它进而满足 <code>RegexComponent</code> 并用在 Regex 构造中。</p>
<pre><code class="language-swift">
func consuming( _ input: String,
startingAt index: String.Index,
in bounds: Range&lt;String.Index&gt;
) throws -&gt; (upperBound: String.Index, output: Self.RegexOutput)?
</code></pre>
<p>返回匹配停止时的上界以及比配得到的结果本身即可。对于这一点WWDC 22 的 <a href="https://developer.apple.com/videos/play/wwdc2022/110358">Swift Regex: Beyond the basics</a> 给了一个非常好的例子:</p>
<pre><code class="language-swift">
import Darwin
struct CDoubleParser: CustomConsumingRegexComponent {
typealias RegexOutput = Double
func consuming( _ input: String, startingAt index: String.Index, in bounds: Range&lt;String.Index&gt;
) throws -&gt; (upperBound: String.Index, output: Double)? {
input[index...].withCString { startAddress in
var endAddress: UnsafeMutablePointer&lt;CChar&gt;!
let output = strtod(startAddress, &amp;endAddress)
guard endAddress &gt; startAddress else { return nil }
let parsedLength = startAddress.distance(to: endAddress)
let upperBound = input.utf8.index(index, offsetBy: parsedLength)
return (upperBound, output)
}
}
}
</code></pre>
<p>在很多情况下,我们可能会进一步地使用 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0299-extend-generic-static-member-lookup.md">protocol 中泛型上下文静态查找</a>的特性,为 <code>RegexComponent</code> 添加类型成员,以便在 <code>Regex</code> 中直接使用:</p>
<pre><code class="language-swift">
extension RegexComponent where Self == CDoubleParser {
static var cDouble: Self { CDoubleParser() }
}
</code></pre>
<p>Foundation 中的各种 parser 基本都遵循了类似的实现方式。</p>
<h2 id="匹配方式"><a class="header" href="#匹配方式">匹配方式</a></h2>
<h3 id="常见的匹配方法"><a class="header" href="#常见的匹配方法">常见的匹配方法</a></h3>
<pre><code class="language-swift">
// 匹配所有可能项,并将全部结果返回
input.matches(of: regex) // [Regex&lt;Output&gt;.Match]
// 匹配时返回第一个结果
input.firstMatch(of: regex) // Regex&lt;Output&gt;.Match?
// 整个字符串能完整匹配时才返回结果
input.wholeMatch(of: regex) // Regex&lt;Output&gt;.Match?
// 字符串的开始部分匹配的话返回结果
// 如果只需要判断是否匹配,使用 `start(with:)`
input.prefixMatch(of: regex) // Regex&lt;Output&gt;.Match?
</code></pre>
<p>匹配后得到的结果中,<code>.0</code> 返回匹配到的整个字符串,从 <code>.1</code> 开始是捕获的组:</p>
<pre><code class="language-swift">
let regex = /Welcome to (.+?), a person blog from (\d+)/
let text = "Welcome to OneV's Den, a person blog from 2011"
if let result = text.wholeMatch(of: regex) {
print("Title: (result.1)") // OneV's Den
print("Year: (result.2)") // 2011
}
</code></pre>
<p><code>Regex.Match</code> 实现了 dynamic lookup可以使用 <code>Reference</code> 直接获取命名的捕获:</p>
<pre><code class="language-swift">
let regex = /Welcome to (?&lt;name&gt;.+?), a person blog from (?&lt;year&gt;\d+)/
let text = "Welcome to OneV's Den, a person blog from 2011"
if let result = text.wholeMatch(of: regex) {
print("Title: (result.name)") // OneV's Den
print("Year: (result.year)") // 2011
}
</code></pre>
<h3 id="基于-regex-的字符串算法操作"><a class="header" href="#基于-regex-的字符串算法操作">基于 Regex 的字符串算法/操作</a></h3>
<ul>
<li><code>input.ranges(of: regex)</code></li>
<li><code>input.replacing(regex, with: "string")</code></li>
<li><code>input.trimmingPrefix(regex)</code>
等..原来在 <code>Collection</code> 中可以针对字符串的操作,可以找到对应的 <code>Regex</code> 版本。</li>
</ul>
<h3 id="regex-变换flags"><a class="header" href="#regex-变换flags">Regex 变换/Flags</a></h3>
<p>在创建 <code>Regex</code> 后,可以使用其上的实例方法来对 <code>Regex</code> 进行部分修改。最常用的大概有:</p>
<ul>
<li><code>ignoresCase(_:)</code> - 匹配是否忽略大小写。等效于 <code>/[aeiou]/i</code> 中的 <code>i</code> flag。</li>
<li><code>anchorsMatchLineEndings(_:)</code> - <code>^</code> 和 <code>$</code> 是否也匹配每行。等效于 <code>/^[aeiou]$/m</code> 中的 <code>m</code> flag。</li>
<li><code>dotMatchesNewlines(_:)</code> - 字面量 <code>.</code> 是否应该匹配包括换行符在内的任意字符。等效于 <code>s</code> flag。</li>
</ul>
<h2 id="小结"><a class="header" href="#小结">小结</a></h2>
<p>Swift Regex 是符合 Swift 美学的正则写法,可以在标准库层面替代掉 Apple 平台上原有的被诟病已久的 <code>NSRegularExpression</code>。随着时代车轮的前行,<code>NSRegularExpression</code> 肯定将被逐渐扫进垃圾堆。
当前 Swift Regex 已经相对很完善了,它的优点非常明确:</p>
<ul>
<li>使用 DSL 的方式构建易于理解和维护的正则</li>
<li>可以与 Parser 结合使用,提供高质量的匹配
当然,在本文写作时也还存在一些不足。</li>
<li>文档不足,实际用例和社区支持也相对匮乏</li>
<li>需求的系统版本较高,近几年内可能难以完全迁移</li>
<li>不论字面量还是 DSL暂时还不支持 <code>if</code> 等条件控制</li>
<li>Foundation 的 Parser 数量和种类不多
不过这些毒点相对都是容易改善的,个人还是十分看好 Swift Regex 的前景。特别是用来做一些简单的文本处理和本地工具的话,会非常方便。
和 Swift <code>String</code> 一样,<code>Regex</code> 从设计初期就考虑了 Unicode 安全。不过本文暂时没有涉及 Unicode 的处理,日后如果用到再继续补充。</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../../posts/swift/yinci_url.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../posts/ios/how_to_create_beautiful_ios_charts_in_swift.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../../posts/swift/yinci_url.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../posts/ios/how_to_create_beautiful_ios_charts_in_swift.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_line_numbers = true;
</script>
<script>
window.playground_copyable = true;
</script>
<script src="../../ace.js"></script>
<script src="../../editor.js"></script>
<script src="../../mode-rust.js"></script>
<script src="../../theme-dawn.js"></script>
<script src="../../theme-tomorrow_night.js"></script>
<script src="../../elasticlunr.min.js"></script>
<script src="../../mark.min.js"></script>
<script src="../../searcher.js"></script>
<script src="../../clipboard.min.js"></script>
<script src="../../highlight.js"></script>
<script src="../../book.js"></script>
<!-- Custom JS scripts -->
<script src="../../src/js/custom.js"></script>
</div>
</body>
</html>