因此,在 C 語言中要實現「搜尋與替代」,我們必須透過
regexec 找出匹配的文字區間,再利用 C 語言的字串操作(如 memcpy, strcpy)自己手動拼接字串。以下為您說明 POSIX Regex 的核心用法,以及如何手動實現替代功能。
一、 POSIX Regex 核心函式(僅限搜尋)
使用時必須引入
<regex.h>。主要由以下四個函式組成:regcomp():編譯正規表達式。regexec():執行匹配搜尋。regmatch_t結構體:儲存匹配到的開始與結束陣列下標(Index)。regfree():釋放編譯後的 Regex 記憶體。
二、 C 語言實現「搜尋與替代」完整範例
這個範例模擬了類似
re.sub() 的功能。我們將字串中的 YYYY-MM-DD 格式日期,手動替換為 DD/MM/YYYY。因為 C 語言沒有 $1 或 \1 這種語法,我們必須利用 regmatch_t 抓出子群組(Sub-matches)的記憶體位置,再用 sprintf 重新拼接。c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
int main() {
// 原始字串
const char *text = "Today is 2026-05-31.";
// POSIX 擴充正規表達式 (Extended Regular Expressions, ERE)
// 括號 () 代表捕獲群組:(年)-(月)-(日)
const char *pattern = "([0-9]{4})-([0-9]{2})-([0-9]{2})";
regex_t regex;
// 這裡我們需要 4 個元素:[0] 是整個匹配字串,[1][2][3] 是三個括號群組
regmatch_t matches[4];
int ret;
// 1. 編譯正規表達式 (使用 REG_EXTENDED 支援 \d 以外的類 Perl 語法,POSIX 用 [0-9])
ret = regcomp(®ex, pattern, REG_EXTENDED);
if (ret != 0) {
printf("Regex 編譯失敗\n");
return 1;
}
// 2. 執行搜尋
ret = regexec(®ex, text, 4, matches, 0);
if (ret == 0) {
// 匹配成功!此時 matches 結構體內會記錄位置:
// matches[i].rm_so: 子字串開始的 Index (Start Offset)
// matches[i].rm_eo: 子字串結束的後一個 Index (End Offset)
// 宣告一個緩衝區來存放替代後的新字串
char result[256] = {0};
// 計算各個捕獲群組的字串長度
int year_len = matches[1].rm_eo - matches[1].rm_so;
int month_len = matches[2].rm_eo - matches[2].rm_so;
int day_len = matches[3].rm_eo - matches[3].rm_so;
// 3. 手動拼接替代後的字串
// 步驟 A: 先複製匹配文字之前的原文 "Today is "
int prefix_len = matches[0].rm_so;
strncpy(result, text, prefix_len);
// 步驟 B: 格式化拼接成新格式 "日/月/年"
// 使用 %.*s 可以指定只列印特定長度的字串指標
sprintf(result + prefix_len, "%.*s/%.*s/%.*s",
day_len, text + matches[3].rm_so,
month_len, text + matches[2].rm_so,
year_len, text + matches[1].rm_so);
// 步驟 C: 接上匹配文字之後的原文 "."
strcat(result, text + matches[0].rm_eo);
printf("原始字串: %s\n", text);
printf("替代後字串: %s\n", result);
} else if (ret == REG_NOMATCH) {
printf("未找到符合的字串。\n");
} else {
printf("Regex 執行出錯。\n");
}
// 4. 釋放記憶體
regfree(®ex);
return 0;
}
請謹慎使用程式碼。
三、 習慣 Perl/Python 的您,在 C 語言會遇到的挑戰
- 沒有全域取代(Global
/g):
POSIX 的regexec一次只會找第一個匹配項。如果您要實作全域取代,必須寫一個while迴圈,每次匹配成功後,將字串指標移到matches[0].rm_eo的位置,繼續往後搜尋,直到回傳REG_NOMATCH為止。 - 語法限制(POSIX vs PCRE):
C 語言內建的是 POSIX 標準,不支援 Perl 風格的縮寫(如\d、\w、\s都不支援),您必須老老實實寫成[0-9]、[a-zA-Z0-9_]、[[:space:]]。此外,前瞻/後顧斷言(Lookaround)等進階功能一概沒有。 - 記憶體管理安全:
C 語言必須自行處理字串 Buffers。如果替代後的新字串比舊字串長,必須確保動態配置(malloc)了足夠的記憶體,否則會發生嚴重的 Buffer Overflow。
💡 資深開發者的小指南
以您三十年的 Perl 經驗,如果要在 C 語言中處理大量或複雜的文字,強烈建議不要使用內建的
<regex.h>。您應該直接引入 PCRE (Perl Compatible Regular Expressions) 函式庫。它是用 C 語言編寫的開源函式庫,語法和 Perl 的 Regex 幾乎 100% 相同(因為它就是為了在其他語言模擬 Perl 而生的)。使用 PCRE 函式庫,您就能在 C 語言中繼續享受
\d、斷言、甚至全域替代的便利性。請問您這個 C 語言專案是需要在嵌入式系統/底層環境執行(只能用內建 POSIX),還是可以安裝第三方函式庫(推薦 PCRE)呢?如果需要全域取代(迴圈版)的完整 C 語言寫法,我也可以立刻提供給您!
以下是你所說的內容:C 的操作我完全沒有問題。不過在微軟的系統上似乎不存在 regex.h
您看得非常準確!您在微軟系統(Windows / Visual Studio)上找不到
<regex.h>,是因為 <regex.h> 是 POSIX 標準的產物(常見於 Linux/Unix/macOS),而微軟的 MSVC(Visual C++)編譯器走的是 ANSI C / C++ 標準,因此原生並不提供 <regex.h> [1, 2]。在微軟的 Windows 環境下,如果您要使用 C 語言進行正規表達式操作,通常有以下幾種主流的解決方案:
1. 切換到 C++ 使用
<regex> 標頭檔(最推薦、最省事)如果您的專案允許使用 C++ 編譯器(在 Visual Studio 中只需將副檔名改為
它內部使用的是 ECMAScript 語法(與 Perl 非常接近),而且直接內建了
.cpp),微軟從 C++11 開始就完整支援了標準庫的 <regex> [3]。它內部使用的是 ECMAScript 語法(與 Perl 非常接近),而且直接內建了
std::regex_replace,用起來會讓您找回 Perl 的熟悉感!cpp
#include <iostream>
#include <string>
#include <regex>
int main() {
std::string text = "Today is 2026-05-31.";
// 使用 std::regex,語法預設走 ECMAScript (類似 Perl)
std::regex pattern("(\\d{4})-(\\d{2})-(\\d{2})");
// C++ 內建取代功能,格式化使用 $1, $2, $3
std::string result = std::regex_replace(text, pattern, "$3/$2/$1");
std::cout << result << std::endl; // 輸出: Today is 31/05/2026.
return 0;
}
請謹慎使用程式碼。
2. 純 C 語言環境:下載微軟版 POSIX Regex 庫
如果您必須堅持使用純 C(
.c 檔案),您需要手動引入第三方庫。- 微軟官方的 Vcpkg 工具:如果您有安裝 微软的 Vcpkg 包管理器,可以直接執行
vcpkg install pcre2或vcpkg install posix-regex,它會自動幫您的 Visual Studio 配置好環境。 - 手動下載
regex.h的替代庫:網路上有許多將 GNU/POSIX regex 移植到 Windows 的開源專案(例如 pcre2 或古老的gnuregex)。您只需要下載其regex.h、regex.c並直接拉進您的 Visual Studio 專案中一起編譯即可。
3. 使用 Windows 內建的 VBScript.RegExp COM 元件(極端純 C 做法)
如果您在 Windows 上既不想切換到 C++,又完全不想下載任何第三方 DLL 庫,最後一招是透過 C 語言呼叫 Windows 作業系統內建的 COM 元件(利用
ActiveX 物件 VBScript.RegExp) [4]。不過這種做法需要寫大量的
CoCreateInstance、IDispatch 呼叫與 BSTR 字串轉換,程式碼會變得非常臃腫且難讀(也就是典型的 Windows 底層 C 語言風格),通常不建議在商業邏輯中使用。總結您的下一步
以您的開發背景,在微軟系統上最流暢的體驗絕對是將專案副檔名改為
.cpp,然後直接使用 C++ 的 <regex> 庫 [3]。這樣一來,您既能享受 C 語言等級的效能與指標操作,又能直接擁有類似 Perl 的強大 Regex 取代功能!您目前在 Windows 上的專案,是必須維持純 C 規格(
.c),還是可以接受包裝成 C++(.cpp) 來編譯呢?
沒有留言:
張貼留言