Ver3.0 ๐ C์ธ์ด๋ก ๋ง๋๋ ์ ๊ทํํ์ ํจํด ๋งค์นญ ์์ง: ํ ์คํธ ์ฒ๋ฆฌ์ ๋ง๋ฒ์ฌ ๋๊ธฐ

๐ C์ธ์ด๋ก ๋ง๋๋ ์ ๊ทํํ์ ํจํด ๋งค์นญ ์์ง: ํ ์คํธ ์ฒ๋ฆฌ์ ๋ง๋ฒ์ฌ ๋๊ธฐ
๋ฌธ์์ด ์์์ ์ํ๋ ํจํด์ ์ฐพ์๋ด๋ ๊ฐ๋ ฅํ ํ ์คํธ ์ฒ๋ฆฌ ์์ง ๊ฐ๋ฐ ์๋ฒฝ ๊ฐ์ด๋
๐ฌ ์๋
! ์ค๋์ ์ ๋ง ์ฌ๋ฏธ์๋ ์ฃผ์ ๋ฅผ ๊ฐ์ง๊ณ ์์ด. C์ธ์ด๋ก ์ ๊ทํํ์ ํจํด ๋งค์นญ ์์ง์ ์ง์ ๋ง๋๋ ๊ฑฐ์ผ! "๋ญ? ๊ทธ๊ฒ ๊ฐ๋ฅํด?"๋ผ๊ณ ์๊ฐํ ์๋ ์๋๋ฐ, ์๊ฐ๋ณด๋ค ์๋ฆฌ๋ ๋จ์ํ๋ฉด์๋ ์์ฒญ๋๊ฒ ๊ฐ๋ ฅํ ๋๊ตฌ๋ฅผ ๋ง๋ค ์ ์์ด. ๐
์ ๊ทํํ์(Regular Expression)์ ํ
์คํธ ์ฒ๋ฆฌ์ ํต์ฌ ๋๊ตฌ์ง. ์ด๋ฉ์ผ ๊ฒ์ฆ๋ถํฐ ๋ก๊ทธ ํ์ผ ๋ถ์, ๋ฐ์ดํฐ ์ถ์ถ๊น์ง ์ ๋ง ๋ค์ํ ๊ณณ์์ ์ฐ์ด๊ฑฐ๋ . ๊ทธ๋ฐ๋ฐ ์ด๊ฑธ C์ธ์ด๋ก ์ง์ ๊ตฌํํ๋ค? ์์ ๋ฉ์ง ์ผ์ด์ผ! ๐
๐ฏ ์ ๊ทํํ์ ์์ง, ์ ์ง์ ๋ง๋ค์ด์ผ ํ ๊น?
๐ค "๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฐ๋ฉด ๋๋ ๊ฑฐ ์๋?"
๋ง์, PCRE๋ POSIX regex ๊ฐ์ ํ๋ฅญํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ์์ด. ํ์ง๋ง ์ง์ ๋ง๋ค์ด๋ณด๋ฉด ์ป๋ ๊ฒ ์ ๋ง ๋ง์:
โ
์๊ณ ๋ฆฌ์ฆ ์ดํด๋ ํฅ์ - ์คํ ๋งํ ์ด๋ก ๊ณผ ์ปดํ์ผ๋ฌ ์๋ฆฌ๋ฅผ ์ค์ ์ผ๋ก ๋ฐฐ์ธ ์ ์์ด
โ
์ปค์คํฐ๋ง์ด์ง ์์ ๋ - ํน์ ๋๋ฉ์ธ์ ์ต์ ํ๋ ํจํด ๋งค์นญ ๊ฐ๋ฅ
โ
์ฑ๋ฅ ์ต์ ํ - ํ์ํ ๊ธฐ๋ฅ๋ง ๊ตฌํํด์ ๊ฐ๋ณ๊ณ ๋น ๋ฅด๊ฒ
โ
์๋ฒ ๋๋ ํ๊ฒฝ - ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์กด์ฑ ์์ด ์ฌ์ฉ ๊ฐ๋ฅ
โ
ํ์ต ํจ๊ณผ - ๋ฌธ์์ด ์ฒ๋ฆฌ์ ์ํ ๋จธ์ ์ ๋ํ ๊น์ ์ดํด
ํนํ ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ํ๋ก๊ทธ๋๋ฐ ์ฌ๋ฅ์ ๊ณต์ ํ๊ฑฐ๋ ๋ฐฐ์ธ ๋, ์ด๋ฐ ์ ์์ค ๊ตฌํ ๊ฒฝํ์ ์ ๋ง ํฐ ์์ฐ์ด ๋ผ. ๋จ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐ๋ ๊ฒ๊ณผ ๋ด๋ถ ๋์ ์๋ฆฌ๋ฅผ ์ดํดํ๋ ๊ฑด ์์ ํ ๋ค๋ฅธ ์ฐจ์์ด๊ฑฐ๋ ! ๐ก
๐งฉ ์ ๊ทํํ์ ๊ธฐ์ด: ํจํด์ ์ธ์ด
๋ณธ๊ฒฉ์ ์ผ๋ก ์์ง์ ๋ง๋ค๊ธฐ ์ ์, ์ ๊ทํํ์์ ๊ธฐ๋ณธ ๋ฌธ๋ฒ๋ถํฐ ์ง๊ณ ๋์ด๊ฐ์. ์ด๊ฒ ์ฐ๋ฆฌ๊ฐ ๊ตฌํํด์ผ ํ ๊ธฐ๋ฅ๋ค์ด์ผ! ๐
๐ค ๊ธฐ๋ณธ ๋ฉํ ๋ฌธ์๋ค
| ๋ฉํ๋ฌธ์ | ์๋ฏธ | ์์ |
|---|---|---|
| . | ์์์ ํ ๋ฌธ์ | "a.c" โ "abc", "axc" ๋งค์นญ |
| * | 0ํ ์ด์ ๋ฐ๋ณต | "ab*c" โ "ac", "abc", "abbc" ๋งค์นญ |
| + | 1ํ ์ด์ ๋ฐ๋ณต | "ab+c" โ "abc", "abbc" ๋งค์นญ (ac๋ ๋ถ๊ฐ) |
| ? | 0ํ ๋๋ 1ํ | "ab?c" โ "ac", "abc" ๋งค์นญ |
| ^ | ๋ฌธ์์ด ์์ | "^hello" โ ๋ฌธ์์ด์ด hello๋ก ์์ |
| $ | ๋ฌธ์์ด ๋ | "world$" โ ๋ฌธ์์ด์ด world๋ก ๋๋จ |
| [] | ๋ฌธ์ ํด๋์ค | "[abc]" โ a, b, c ์ค ํ๋ |
| | | OR ์ฐ์ฐ | "cat|dog" โ cat ๋๋ dog |
๐ก ์ค์ ํ
์ฒ์ ์์ง์ ๋ง๋ค ๋๋ ๋ชจ๋ ๊ธฐ๋ฅ์ ๋ค ๊ตฌํํ๋ ค๊ณ ํ์ง ๋ง! ๋จผ์ ๊ธฐ๋ณธ ๋ฌธ์ ๋งค์นญ, . (์ ), * (๋ณํ) ์ด ์ธ ๊ฐ์ง๋ง ์๋ฒฝํ๊ฒ ๊ตฌํํด๋ด. ์ด๊ฒ๋ง์ผ๋ก๋ ์์ฒญ๋๊ฒ ๋ง์ ํจํด์ ์ฒ๋ฆฌํ ์ ์๊ฑฐ๋ . ๋๋จธ์ง๋ ๋์ค์ ํ์ฅํ๋ฉด ๋ผ! ๐ฏ
๐๏ธ ์์ง ์ํคํ ์ฒ: ์ด๋ป๊ฒ ์ค๊ณํ ๊น?
์ ๊ทํํ์ ์์ง์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋ ๊ฐ์ง์ผ. ๊ฐ๊ฐ ์ฅ๋จ์ ์ด ์์ผ๋๊น ํ๋ก์ ํธ ๋ชฉ์ ์ ๋ง๊ฒ ์ ํํ๋ฉด ๋ผ! ๐ค
๐ ๋ฐฉ๋ฒ 1: NFA (Nondeterministic Finite Automaton) ๊ธฐ๋ฐ
ํน์ง:
โข ๊ตฌํ์ด ์๋์ ์ผ๋ก ๊ฐ๋จํด
โข ๋ฐฑํธ๋ํน์ ์ฌ์ฉํด์ ๋ชจ๋ ๊ฐ๋ฅ์ฑ์ ํ์
โข ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ด์ง๋ง ์ต์
์ ๊ฒฝ์ฐ ๋๋ฆด ์ ์์ด
โข ์บก์ฒ ๊ทธ๋ฃน, ๋ฐฑ๋ ํผ๋ฐ์ค ๊ฐ์ ๊ณ ๊ธ ๊ธฐ๋ฅ ๊ตฌํ์ด ์ฌ์
์ฅ์ :
โ
์ฝ๋๊ฐ ์ง๊ด์ ์ด๊ณ ์ดํดํ๊ธฐ ์ฌ์
โ
ํจํด ์ปดํ์ผ ์๋๊ฐ ๋น ๋ฆ
โ
๋ณต์กํ ๊ธฐ๋ฅ ์ถ๊ฐ๊ฐ ์ฉ์ดํจ
๋จ์ :
โ ํน์ ํจํด์์ ์ง์์ ์๊ฐ ๋ณต์ก๋ ๊ฐ๋ฅ
โ ์ต์
์ ๊ฒฝ์ฐ ์ฑ๋ฅ ์์ธก์ด ์ด๋ ค์
๐ ๋ฐฉ๋ฒ 2: DFA (Deterministic Finite Automaton) ๊ธฐ๋ฐ
ํน์ง:
โข ๊ฐ ์ํ์์ ๋ค์ ์ํ๊ฐ ๋ช
ํํ๊ฒ ๊ฒฐ์ ๋ผ
โข ๋ฐฑํธ๋ํน ์์ด ์ ํ ์๊ฐ์ ๋งค์นญ
โข ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ ๋ง์ด ์ฌ์ฉํ์ง๋ง ์์ธก ๊ฐ๋ฅํ ์ฑ๋ฅ
โข ๊ตฌํ์ด ๋ณต์กํ๊ณ ์ผ๋ถ ๊ณ ๊ธ ๊ธฐ๋ฅ ๊ตฌํ์ด ์ด๋ ค์
์ฅ์ :
โ
๋งค์นญ ์๋๊ฐ ํญ์ O(n) - ์
๋ ฅ ๊ธธ์ด์ ๋น๋ก
โ
์ฑ๋ฅ ์์ธก์ด ์ ํํจ
โ
๋์ฉ๋ ํ
์คํธ ์ฒ๋ฆฌ์ ์ ๋ฆฌ
๋จ์ :
โ ์ํ ํญ๋ฐ ๋ฌธ์ - ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ํด ์ ์์
โ ๊ตฌํ ๋ณต์ก๋๊ฐ ๋์
โ ๋ฐฑ๋ ํผ๋ฐ์ค ๊ฐ์ ๊ธฐ๋ฅ ๊ตฌํ ๋ถ๊ฐ
โ ๏ธ ์ด๋ณด์ ์ถ์ฒ
์ฒ์ ๋ง๋ค์ด๋ณธ๋ค๋ฉด NFA ๋ฐฉ์์ ๊ฐ๋ ฅ ์ถ์ฒํด! ์ฝ๋๊ฐ ํจ์ฌ ์งง๊ณ ์ดํดํ๊ธฐ ์ฌ์์ ํ์ต ํจ๊ณผ๊ฐ ํฌ๊ฑฐ๋ . Rob Pike์ ์ ๋ช
ํ ์ ๊ทํํ์ ๊ตฌํ๋ NFA ๋ฐฉ์์ด๊ณ , ๊ณ ์ 30์ค ์ ๋์ C ์ฝ๋๋ก ํต์ฌ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์ด. ๐
๐ป ์ค์ ๊ตฌํ: ๊ฐ๋จํ ์ ๊ทํํ์ ์์ง ๋ง๋ค๊ธฐ
์, ์ด์ ์ง์ง ์ฝ๋๋ฅผ ์์ฑํด๋ณผ ์๊ฐ์ด์ผ! ๋จผ์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ๋ถํฐ ๊ตฌํํด๋ณด์. ๐
๐ฌ 1๋จ๊ณ: ๊ธฐ๋ณธ ๊ตฌ์กฐ ์ค๊ณ
ํต์ฌ ํจ์ ๊ตฌ์กฐ
// ๋ฉ์ธ ๋งค์นญ ํจ์
int match(char *regexp, char *text);
// ์ฌ๊ท์ ์ผ๋ก ํจํด์ ๊ฒ์ฌํ๋ ํต์ฌ ํจ์
int matchhere(char *regexp, char *text);
// ๋ฌธ์ ๋งค์นญ ๊ฒ์ฌ
int matchchar(int c, char *text);
์ด ์ธ ํจ์๊ฐ ์ฐ๋ฆฌ ์์ง์ ํต์ฌ์ด์ผ. match๋ ์ ์ฒด ํ
์คํธ์์ ํจํด์ ์ฐพ๊ณ , matchhere๋ ํ์ฌ ์์น์์ ํจํด์ด ๋งค์นญ๋๋์ง ํ์ธํ๊ณ , matchchar๋ ๊ฐ๋ณ ๋ฌธ์๋ฅผ ๋น๊ตํด. ๐ฏ
๐จ 2๋จ๊ณ: ๊ธฐ๋ณธ ๋งค์นญ ๊ตฌํ
๊ฐ์ฅ ๊ฐ๋จํ ๋ฒ์ ๋ถํฐ ์์ํด๋ณด์. ์ด๊ฑด ์ ๋ง ๋๋ผ์ธ ์ ๋๋ก ๊ฐ๊ฒฐํด! ๐ฎ
#include <stdio.h>
#include <string.h>
// ๋ฌธ์ c๊ฐ text์ ์ฒซ ๋ฌธ์์ ๋งค์นญ๋๋์ง ํ์ธ
int matchchar(int c, char *text) {
return (*text != '\0' && (*text == c || c == '.'));
}
// ํ์ฌ ์์น์์ ํจํด ๋งค์นญ ์๋
int matchhere(char *regexp, char *text) {
// ํจํด ๋์ ๋๋ฌ - ์ฑ๊ณต!
if (regexp[0] == '\0')
return 1;
// ๋ค์ ๋ฌธ์๊ฐ * (๋ณํ)์ธ ๊ฒฝ์ฐ
if (regexp[1] == '*')
return matchstar(regexp[0], regexp + 2, text);
// $ (๋ฌธ์์ด ๋) ์ฒ๋ฆฌ
if (regexp[0] == '$' && regexp[1] == '\0')
return *text == '\0';
// ์ผ๋ฐ ๋ฌธ์ ๋งค์นญ
if (*text != '\0' && matchchar(regexp[0], text))
return matchhere(regexp + 1, text + 1);
return 0;
}
// * (0ํ ์ด์ ๋ฐ๋ณต) ์ฒ๋ฆฌ
int matchstar(int c, char *regexp, char *text) {
do {
// ๋จผ์ *๋ฅผ 0ํ ๋ฐ๋ณต์ผ๋ก ์ฒ๋ฆฌ
if (matchhere(regexp, text))
return 1;
} while (*text != '\0' && matchchar(c, text++));
return 0;
}
// ๋ฉ์ธ ๋งค์นญ ํจ์
int match(char *regexp, char *text) {
// ^ (๋ฌธ์์ด ์์) ์ฒ๋ฆฌ
if (regexp[0] == '^')
return matchhere(regexp + 1, text);
// ํ
์คํธ์ ๋ชจ๋ ์์น์์ ๋งค์นญ ์๋
do {
if (matchhere(regexp, text))
return 1;
} while (*text++ != '\0');
return 0;
}
// ํ
์คํธ ์ฝ๋
int main() {
printf("Testing regex engine...\n\n");
// ํ
์คํธ ์ผ์ด์ค๋ค
printf("match(\"hello\", \"hello world\"): %d\n",
match("hello", "hello world"));
printf("match(\"^hello\", \"hello world\"): %d\n",
match("^hello", "hello world"));
printf("match(\"world$\", \"hello world\"): %d\n",
match("world$", "hello world"));
printf("match(\"a*b\", \"aaab\"): %d\n",
match("a*b", "aaab"));
printf("match(\"a.c\", \"abc\"): %d\n",
match("a.c", "abc"));
return 0;
}
๐ ์ฝ๋ ์ดํดํ๊ธฐ
์ด ์ฝ๋์ ํต์ฌ์ ์ฌ๊ท์ผ! matchhere ํจ์๊ฐ ์๊ธฐ ์์ ์ ํธ์ถํ๋ฉด์ ํจํด๊ณผ ํ
์คํธ๋ฅผ ํ ๊ธ์์ฉ ์ ์ง์์ผ. matchstar๋ * ๋ฉํ๋ฌธ์๋ฅผ ์ฒ๋ฆฌํ๋๋ฐ, 0ํ๋ถํฐ ์์ํด์ ๊ฐ๋ฅํ ๋ชจ๋ ๋ฐ๋ณต ํ์๋ฅผ ์๋ํด๋ณด๋ ๊ฑฐ์ง. ์ด๊ฒ ๋ฐ๋ก ๋ฐฑํธ๋ํน์ด์ผ! ๐
โก 3๋จ๊ณ: ๊ธฐ๋ฅ ํ์ฅํ๊ธฐ
๊ธฐ๋ณธ ์์ง์ด ์๋ํ๋ฉด, ์ด์ ๋ ๋ง์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์ฐจ๋ก์ผ! ํ๋์ฉ ์ดํด๋ณด์. ๐
๐ + (1ํ ์ด์ ๋ฐ๋ณต) ๊ตฌํ
// + ๋ฉํ๋ฌธ์ ์ฒ๋ฆฌ ํจ์
int matchplus(int c, char *regexp, char *text) {
// ์ต์ 1ํ๋ ๋งค์นญ๋์ด์ผ ํจ
if (*text != '\0' && matchchar(c, text)) {
text++;
// ์ดํ๋ * ์ฒ๋ผ ๋์
do {
if (matchhere(regexp, text))
return 1;
} while (*text != '\0' && matchchar(c, text++));
}
return 0;
}
// matchhere ํจ์์ ์ถ๊ฐ
if (regexp[1] == '+')
return matchplus(regexp[0], regexp + 2, text);
๐ ? (0ํ ๋๋ 1ํ) ๊ตฌํ
// ? ๋ฉํ๋ฌธ์ ์ฒ๋ฆฌ ํจ์
int matchquestion(int c, char *regexp, char *text) {
// 0ํ ๋งค์นญ ์๋
if (matchhere(regexp, text))
return 1;
// 1ํ ๋งค์นญ ์๋
if (*text != '\0' && matchchar(c, text))
return matchhere(regexp, text + 1);
return 0;
}
// matchhere ํจ์์ ์ถ๊ฐ
if (regexp[1] == '?')
return matchquestion(regexp[0], regexp + 2, text);
๐ [] (๋ฌธ์ ํด๋์ค) ๊ตฌํ
// ๋ฌธ์ ํด๋์ค ๋งค์นญ ๊ฒ์ฌ
int matchcharclass(char *pattern, char *text) {
int negate = 0;
int match = 0;
if (*text == '\0')
return 0;
// [^...] ํํ - ๋ถ์
if (*pattern == '^') {
negate = 1;
pattern++;
}
// ] ๊น์ง ๋ฌธ์๋ค ๊ฒ์ฌ
while (*pattern != ']' && *pattern != '\0') {
// ๋ฒ์ ํํ (์: a-z)
if (pattern[1] == '-' && pattern[2] != ']' && pattern[2] != '\0') {
if (*text >= pattern[0] && *text <= pattern[2])
match = 1;
pattern += 3;
} else {
if (*text == *pattern)
match = 1;
pattern++;
}
}
return negate ? !match : match;
}
// matchhere ํจ์์ ์ถ๊ฐ
if (regexp[0] == '[') {
char *end = strchr(regexp, ']');
if (end != NULL) {
if (matchcharclass(regexp + 1, text))
return matchhere(end + 1, text + 1);
return 0;
}
}
๐จ ๊ณ ๊ธ ๊ธฐ๋ฅ: ์บก์ฒ ๊ทธ๋ฃน๊ณผ ๋งค์นญ ์ ๋ณด
์ด์ ์ข ๋ ์ค์ฉ์ ์ธ ๊ธฐ๋ฅ์ ์ถ๊ฐํด๋ณด์. ๋จ์ํ ๋งค์นญ ์ฌ๋ถ๋ง ์๋ ค์ฃผ๋ ๊ฒ ์๋๋ผ, ์ด๋์ ๋งค์นญ๋์๋์ง, ๋ฌด์์ด ๋งค์นญ๋์๋์ง๋ ์๋ ค์ฃผ๋ ๊ฑฐ์ผ! ๐ฏ
๋งค์นญ ๊ฒฐ๊ณผ ๊ตฌ์กฐ์ฒด ์ค๊ณ
#define MAX_GROUPS 10
// ๋งค์นญ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๋ ๊ตฌ์กฐ์ฒด
typedef struct {
int matched; // ๋งค์นญ ์ฑ๊ณต ์ฌ๋ถ
char *start; // ๋งค์นญ ์์ ์์น
char *end; // ๋งค์นญ ๋ ์์น
int group_count; // ์บก์ฒ ๊ทธ๋ฃน ๊ฐ์
struct {
char *start;
char *end;
} groups[MAX_GROUPS]; // ๊ฐ ๊ทธ๋ฃน์ ์์น
} MatchResult;
// ๊ฐ์ ๋ ๋งค์นญ ํจ์
int match_advanced(char *regexp, char *text, MatchResult *result) {
result->matched = 0;
result->group_count = 0;
if (regexp[0] == '^') {
result->start = text;
if (matchhere_advanced(regexp + 1, text, result)) {
result->matched = 1;
return 1;
}
return 0;
}
// ๋ชจ๋ ์์น์์ ์๋
do {
result->start = text;
if (matchhere_advanced(regexp, text, result)) {
result->matched = 1;
return 1;
}
} while (*text++ != '\0');
return 0;
}
์ด๋ ๊ฒ ํ๋ฉด ๋งค์นญ๋ ๋ถ๋ถ ๋ฌธ์์ด์ ์ถ์ถํ ์ ์์ด! ์๋ฅผ ๋ค์ด ์ด๋ฉ์ผ ์ฃผ์์์ ์ฌ์ฉ์๋ช ๊ณผ ๋๋ฉ์ธ์ ๋ฐ๋ก ๋ฝ์๋ผ ์ ์๋ ๊ฑฐ์ง. ๐ง
๐ง ์ต์ ํ ๊ธฐ๋ฒ: ๋ ๋น ๋ฅด๊ณ ํจ์จ์ ์ผ๋ก
๊ธฐ๋ณธ ๊ตฌํ์ด ์์ฑ๋์๋ค๋ฉด, ์ด์ ์ฑ๋ฅ์ ๊ฐ์ ํ ์ฐจ๋ก์ผ. ์ค์ ์์ ์ธ ์ ์๋ ์ต์ ํ ๊ธฐ๋ฒ๋ค์ ์์๋ณด์! โก
๐ 1. ํจํด ์ฌ์ ์ปดํ์ผ
๊ฐ์ ํจํด์ ์ฌ๋ฌ ๋ฒ ์ฌ์ฉํ๋ค๋ฉด, ๋งค๋ฒ ํ์ฑํ๋ ๊ฑด ๋นํจ์จ์ ์ด์ผ. ํจํด์ ํ ๋ฒ๋ง ์ปดํ์ผํด์ ์ฌ์ฌ์ฉํ์!
// ์ปดํ์ผ๋ ํจํด ๊ตฌ์กฐ์ฒด
typedef struct {
char *original; // ์๋ณธ ํจํด
int *bytecode; // ์ปดํ์ผ๋ ๋ฐ์ดํธ์ฝ๋
int bytecode_len; // ๋ฐ์ดํธ์ฝ๋ ๊ธธ์ด
int flags; // ์ต์
ํ๋๊ทธ
} CompiledPattern;
// ํจํด ์ปดํ์ผ ํจ์
CompiledPattern* compile_pattern(char *regexp, int flags) {
CompiledPattern *cp = malloc(sizeof(CompiledPattern));
cp->original = strdup(regexp);
cp->flags = flags;
// ํจํด์ ๋ฐ์ดํธ์ฝ๋๋ก ๋ณํ
// (์ค์ ๊ตฌํ์ ๋ณต์กํ์ง๋ง ๊ฐ๋
์ ๊ฐ๋จํด)
cp->bytecode = parse_to_bytecode(regexp, &cp->bytecode_len);
return cp;
}
// ์ปดํ์ผ๋ ํจํด์ผ๋ก ๋งค์นญ
int match_compiled(CompiledPattern *cp, char *text) {
return execute_bytecode(cp->bytecode, cp->bytecode_len, text);
}
// ์ฌ์ฉ ์์
CompiledPattern *email_pattern = compile_pattern(
"[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-z]+", 0
);
// ์ฌ๋ฌ ํ
์คํธ์ ์ฌ์ฌ์ฉ
match_compiled(email_pattern, "test@example.com");
match_compiled(email_pattern, "user@domain.org");
๐ 2. ๋ฉ๋ชจ์ด์ ์ด์ (Memoization)
๊ฐ์ (ํจํด ์์น, ํ ์คํธ ์์น) ์กฐํฉ์ ์ฌ๋ฌ ๋ฒ ๊ฒ์ฌํ๋ ๊ฑธ ๋ฐฉ์งํ์!
#define MEMO_SIZE 1024
// ๋ฉ๋ชจ์ด์ ์ด์
ํ
์ด๋ธ
typedef struct {
char *regexp_pos;
char *text_pos;
int result;
int valid;
} MemoEntry;
MemoEntry memo_table[MEMO_SIZE];
// ํด์ ํจ์
unsigned int memo_hash(char *regexp, char *text) {
unsigned long h = 5381;
h = ((h << 5) + h) + (unsigned long)regexp;
h = ((h << 5) + h) + (unsigned long)text;
return h % MEMO_SIZE;
}
// ๋ฉ๋ชจ์ด์ ์ด์
์ ์ฌ์ฉํ๋ matchhere
int matchhere_memo(char *regexp, char *text) {
unsigned int hash = memo_hash(regexp, text);
// ์บ์ ํ์ธ
if (memo_table[hash].valid &&
memo_table[hash].regexp_pos == regexp &&
memo_table[hash].text_pos == text) {
return memo_table[hash].result;
}
// ์ค์ ๋งค์นญ ์ํ
int result = matchhere(regexp, text);
// ๊ฒฐ๊ณผ ์บ์ฑ
memo_table[hash].regexp_pos = regexp;
memo_table[hash].text_pos = text;
memo_table[hash].result = result;
memo_table[hash].valid = 1;
return result;
}
์ด ๊ธฐ๋ฒ์ ํนํ ๋ณต์กํ ํจํด์์ ์์ฒญ๋ ์ฑ๋ฅ ํฅ์์ ๊ฐ์ ธ์! ๐
๐ 3. ์กฐ๊ธฐ ์ข ๋ฃ ์ต์ ํ
๋งค์นญ์ด ๋ถ๊ฐ๋ฅํ ๊ฒฝ์ฐ๋ฅผ ๋นจ๋ฆฌ ํ๋จํด์ ๋ถํ์ํ ์ฐ์ฐ์ ์ค์ด์!
// ํจํด ๋ถ์ ์ ๋ณด
typedef struct {
int min_length; // ์ต์ ๋งค์นญ ๊ธธ์ด
int max_length; // ์ต๋ ๋งค์นญ ๊ธธ์ด (-1์ด๋ฉด ๋ฌด์ ํ)
char first_chars[256]; // ๊ฐ๋ฅํ ์ฒซ ๋ฌธ์๋ค
int has_anchor; // ^ ๋๋ $ ํฌํจ ์ฌ๋ถ
} PatternInfo;
// ํจํด ์ฌ์ ๋ถ์
PatternInfo analyze_pattern(char *regexp) {
PatternInfo info = {0};
// ์ต์/์ต๋ ๊ธธ์ด ๊ณ์ฐ
// ๊ฐ๋ฅํ ์ฒซ ๋ฌธ์ ์งํฉ ๊ตฌ์ถ
// ์ต์ปค ์กด์ฌ ์ฌ๋ถ ํ์ธ
return info;
}
// ์ต์ ํ๋ ๋งค์นญ
int match_optimized(char *regexp, char *text, PatternInfo *info) {
int text_len = strlen(text);
// ํ
์คํธ๊ฐ ๋๋ฌด ์งง์ผ๋ฉด ์ฆ์ ์คํจ
if (text_len < info->min_length)
return 0;
// ์ฒซ ๋ฌธ์ ๋น ๋ฅธ ๊ฒ์ฌ
if (info->first_chars[(unsigned char)*text] == 0)
return 0;
// ์ค์ ๋งค์นญ ์ํ
return match(regexp, text);
}
๐ ๏ธ ์ค์ ํ์ฉ: ํ ์คํธ ์ฒ๋ฆฌ ์์ง ๋ง๋ค๊ธฐ
์ด์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ ์ ๊ทํํ์ ์์ง์ ์ค์ ๋ก ํ์ฉํด๋ณด์! ๋ช ๊ฐ์ง ์ค์ฉ์ ์ธ ์์ ๋ฅผ ์ดํด๋ณผ๊ฒ. ๐ผ
๐ง ์์ 1: ์ด๋ฉ์ผ ๊ฒ์ฆ๊ธฐ
#include <stdio.h>
#include <string.h>
// ์ด๋ฉ์ผ ํจํด: ๊ฐ๋จํ ๋ฒ์
// [a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-z]+
int validate_email(char *email) {
// ์ค์ ๋ก๋ ๋ ๋ณต์กํ ํจํด์ด ํ์ํ์ง๋ง ์์๋ก ๋จ์ํ
char *pattern = "[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}";
return match(pattern, email);
}
// ํ
์คํธ
int main() {
char *test_emails[] = {
"user@example.com", // ์ ํจ
"test.user@domain.co.kr", // ์ ํจ
"invalid@", // ๋ฌดํจ
"@invalid.com", // ๋ฌดํจ
"no-at-sign.com" // ๋ฌดํจ
};
printf("์ด๋ฉ์ผ ๊ฒ์ฆ ํ
์คํธ:\n\n");
for (int i = 0; i < 5; i++) {
int valid = validate_email(test_emails[i]);
printf("%s: %s\n", test_emails[i],
valid ? "โ ์ ํจ" : "โ ๋ฌดํจ");
}
return 0;
}
๐ ์์ 2: ๋ก๊ทธ ํ์ผ ํ์
// ๋ก๊ทธ ์ํธ๋ฆฌ ๊ตฌ์กฐ์ฒด
typedef struct {
char timestamp[32];
char level[16];
char message[256];
} LogEntry;
// ๋ก๊ทธ ํจํด: [2024-01-15 10:30:45] ERROR: Something went wrong
int parse_log_line(char *line, LogEntry *entry) {
// ํ์์คํฌํ ์ถ์ถ
char *ts_pattern = "\\[[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\]";
MatchResult ts_result;
if (!match_advanced(ts_pattern, line, &ts_result))
return 0;
// ํ์์คํฌํ ๋ณต์ฌ (๋๊ดํธ ์ ์ธ)
int ts_len = ts_result.end - ts_result.start - 2;
strncpy(entry->timestamp, ts_result.start + 1, ts_len);
entry->timestamp[ts_len] = '\0';
// ๋ก๊ทธ ๋ ๋ฒจ ์ถ์ถ
char *level_pattern = "(ERROR|WARNING|INFO|DEBUG)";
MatchResult level_result;
if (!match_advanced(level_pattern, ts_result.end, &level_result))
return 0;
int level_len = level_result.end - level_result.start;
strncpy(entry->level, level_result.start, level_len);
entry->level[level_len] = '\0';
// ๋ฉ์์ง ์ถ์ถ (: ์ดํ ๋ถ๋ถ)
char *msg_start = strchr(level_result.end, ':');
if (msg_start) {
msg_start += 2; // ": " ๊ฑด๋๋ฐ๊ธฐ
strncpy(entry->message, msg_start, sizeof(entry->message) - 1);
}
return 1;
}
// ๋ก๊ทธ ํ์ผ ์ฒ๋ฆฌ
void process_log_file(char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) {
printf("ํ์ผ์ ์ด ์ ์์ต๋๋ค: %s\n", filename);
return;
}
char line[512];
LogEntry entry;
int line_num = 0;
int error_count = 0;
printf("๋ก๊ทธ ํ์ผ ๋ถ์ ์ค...\n\n");
while (fgets(line, sizeof(line), fp)) {
line_num++;
if (parse_log_line(line, &entry)) {
printf("[%d] %s [%s] %s\n",
line_num, entry.timestamp, entry.level, entry.message);
if (strcmp(entry.level, "ERROR") == 0)
error_count++;
}
}
printf("\n์ด %d๊ฐ ๋ผ์ธ ์ฒ๋ฆฌ, %d๊ฐ ์๋ฌ ๋ฐ๊ฒฌ\n", line_num, error_count);
fclose(fp);
}
๐ ์์ 3: ํ ์คํธ ์นํ ์์ง
// ํจํด ๋งค์นญ ํ ์นํ
char* replace_pattern(char *text, char *pattern, char *replacement) {
static char result[4096];
char *result_ptr = result;
char *text_ptr = text;
MatchResult match;
while (*text_ptr) {
// ํ์ฌ ์์น์์ ํจํด ๋งค์นญ ์๋
if (match_advanced(pattern, text_ptr, &match)) {
// ์นํ ๋ฌธ์์ด ๋ณต์ฌ
strcpy(result_ptr, replacement);
result_ptr += strlen(replacement);
// ๋งค์นญ๋ ๋ถ๋ถ ๊ฑด๋๋ฐ๊ธฐ
text_ptr = match.end;
} else {
// ๋งค์นญ ์ ๋๋ฉด ์๋ณธ ๋ฌธ์ ๋ณต์ฌ
*result_ptr++ = *text_ptr++;
}
}
*result_ptr = '\0';
return result;
}
// ์ฌ์ฉ ์์
int main() {
char *text = "์ ํ๋ฒํธ: 010-1234-5678, 010-9876-5432";
// ์ ํ๋ฒํธ ๋ง์คํน
char *pattern = "[0-9]{3}-[0-9]{4}-[0-9]{4}";
char *replacement = "***-****-****";
char *masked = replace_pattern(text, pattern, replacement);
printf("์๋ณธ: %s\n", text);
printf("๋ง์คํน: %s\n", masked);
return 0;
}
๐ ๋๋ฒ๊น ๊ณผ ํ ์คํธ: ๊ฒฌ๊ณ ํ ์์ง ๋ง๋ค๊ธฐ
์ ๊ทํํ์ ์์ง์ ๋ณต์กํ ๋ก์ง ๋๋ฌธ์ ๋ฒ๊ทธ๊ฐ ์๊ธฐ๊ธฐ ์ฌ์. ์ฒ ์ ํ ํ ์คํธ๊ฐ ํ์์ผ! ๐
๐งช ํ ์คํธ ํ๋ ์์ํฌ ๊ตฌ์ถ
// ํ
์คํธ ์ผ์ด์ค ๊ตฌ์กฐ์ฒด
typedef struct {
char *pattern;
char *text;
int expected;
char *description;
} TestCase;
// ํ
์คํธ ์คํ ํจ์
void run_tests(TestCase *tests, int count) {
int passed = 0;
int failed = 0;
printf("=== ์ ๊ทํํ์ ์์ง ํ
์คํธ ===\n\n");
for (int i = 0; i < count; i++) {
int result = match(tests[i].pattern, tests[i].text);
int success = (result == tests[i].expected);
if (success) {
passed++;
printf("โ ");
} else {
failed++;
printf("โ ");
}
printf("ํ
์คํธ %d: %s\n", i + 1, tests[i].description);
printf(" ํจํด: \"%s\", ํ
์คํธ: \"%s\"\n",
tests[i].pattern, tests[i].text);
printf(" ์์: %d, ๊ฒฐ๊ณผ: %d\n\n",
tests[i].expected, result);
}
printf("=== ํ
์คํธ ๊ฒฐ๊ณผ ===\n");
printf("ํต๊ณผ: %d/%d\n", passed, count);
printf("์คํจ: %d/%d\n", failed, count);
printf("์ฑ๊ณต๋ฅ : %.1f%%\n", (passed * 100.0) / count);
}
// ํ
์คํธ ์ผ์ด์ค ์ ์
TestCase basic_tests[] = {
{"abc", "abc", 1, "์ ํํ ๋ฌธ์์ด ๋งค์นญ"},
{"abc", "abcd", 1, "๋ถ๋ถ ๋ฌธ์์ด ๋งค์นญ"},
{"abc", "xabc", 1, "์ค๊ฐ์ ํจํด ์กด์ฌ"},
{"abc", "ab", 0, "๋ถ์์ ํ ๋งค์นญ"},
{"^abc", "abc", 1, "๋ฌธ์์ด ์์ ์ต์ปค"},
{"^abc", "xabc", 0, "์์ ์ต์ปค ์คํจ"},
{"abc$", "abc", 1, "๋ฌธ์์ด ๋ ์ต์ปค"},
{"abc$", "abcd", 0, "๋ ์ต์ปค ์คํจ"},
{"a.c", "abc", 1, "์ ๋ฉํ๋ฌธ์"},
{"a.c", "axc", 1, "์ ๋ฉํ๋ฌธ์ (๋ค๋ฅธ ๋ฌธ์)"},
{"a*b", "b", 1, "๋ณํ 0ํ ๋ฐ๋ณต"},
{"a*b", "ab", 1, "๋ณํ 1ํ ๋ฐ๋ณต"},
{"a*b", "aaab", 1, "๋ณํ ์ฌ๋ฌ ๋ฒ ๋ฐ๋ณต"},
{"a+b", "b", 0, "ํ๋ฌ์ค 0ํ ์คํจ"},
{"a+b", "ab", 1, "ํ๋ฌ์ค 1ํ ์ฑ๊ณต"},
{"a?b", "b", 1, "๋ฌผ์ํ 0ํ"},
{"a?b", "ab", 1, "๋ฌผ์ํ 1ํ"},
{"[abc]", "a", 1, "๋ฌธ์ ํด๋์ค ๋งค์นญ"},
{"[abc]", "d", 0, "๋ฌธ์ ํด๋์ค ๋ถ์ผ์น"},
{"[a-z]", "m", 1, "๋ฒ์ ํํ"},
};
int main() {
run_tests(basic_tests, sizeof(basic_tests) / sizeof(TestCase));
return 0;
}
๐ก ํ
์คํธ ์์ฑ ํ
โข ๊ฒฝ๊ณ ์กฐ๊ฑด์ ๊ผญ ํ
์คํธํด! ๋น ๋ฌธ์์ด, ๋งค์ฐ ๊ธด ๋ฌธ์์ด, ํน์ ๋ฌธ์ ๋ฑ
โข ์ฑ๋ฅ ํ
์คํธ๋ ์ค์ํด. ํน์ ํจํด์์ ์ง์์ ์ผ๋ก ๋๋ ค์ง๋์ง ํ์ธ
โข ๋ฉ๋ชจ๋ฆฌ ๋์ ์ฒดํฌ๋ ํ์! Valgrind ๊ฐ์ ๋๊ตฌ ํ์ฉ
โข ์ค์ ๋ฐ์ดํฐ๋ก ํ
์คํธํด๋ด. ์ด๋ก ๊ณผ ์ค์ ์ ๋ค๋ฅด๊ฑฐ๋ !
๐ ๋๋ฒ๊น ๋๊ตฌ ๋ง๋ค๊ธฐ
// ๋๋ฒ๊ทธ ๋ชจ๋ ํ๋๊ทธ
int debug_mode = 0;
// ๋๋ฒ๊ทธ ์ถ๋ ฅ ํจ์
void debug_print(int level, char *format, ...) {
if (!debug_mode)
return;
// ๋ค์ฌ์ฐ๊ธฐ๋ก ์ฌ๊ท ๊น์ด ํํ
for (int i = 0; i < level; i++)
printf(" ");
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
printf("\n");
}
// ๋๋ฒ๊ทธ ์ ๋ณด๋ฅผ ์ถ๋ ฅํ๋ matchhere
int matchhere_debug(char *regexp, char *text, int level) {
debug_print(level, "matchhere(\"%s\", \"%s\")", regexp, text);
if (regexp[0] == '\0') {
debug_print(level, "โ ํจํด ๋ ๋๋ฌ, ์ฑ๊ณต!");
return 1;
}
if (regexp[1] == '*') {
debug_print(level, "โ * ๋ฉํ๋ฌธ์ ์ฒ๋ฆฌ");
return matchstar_debug(regexp[0], regexp + 2, text, level + 1);
}
if (regexp[0] == '$' && regexp[1] == '\0') {
int result = (*text == '\0');
debug_print(level, "โ $ ์ต์ปค: %s", result ? "์ฑ๊ณต" : "์คํจ");
return result;
}
if (*text != '\0' && matchchar(regexp[0], text)) {
debug_print(level, "โ '%c' ๋งค์นญ ์ฑ๊ณต, ๋ค์์ผ๋ก", *text);
return matchhere_debug(regexp + 1, text + 1, level + 1);
}
debug_print(level, "โ ๋งค์นญ ์คํจ");
return 0;
}
// ์ฌ์ฉ ์์
int main() {
debug_mode = 1; // ๋๋ฒ๊ทธ ๋ชจ๋ ํ์ฑํ
printf("=== ๋๋ฒ๊ทธ ์ถ์ ===\n\n");
match_debug("a*b", "aaab");
return 0;
}
์ด๋ ๊ฒ ํ๋ฉด ๋งค์นญ ๊ณผ์ ์ ๋จ๊ณ๋ณ๋ก ๋ณผ ์ ์์ด์ ๋ฌธ์ ๋ฅผ ์ฐพ๊ธฐ๊ฐ ํจ์ฌ ์ฌ์์ ธ! ๐ฏ
๐ ์ค์ ํ๋ก์ ํธ: ์์ ํ ํ ์คํธ ์ฒ๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
์ง๊ธ๊น์ง ๋ฐฐ์ด ๊ฑธ ๋ชจ๋ ํฉ์ณ์ ์ค์ฉ์ ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค์ด๋ณด์! ์ด๊ฑด ์ค์ ํ๋ก์ ํธ์์ ๋ฐ๋ก ์ธ ์ ์๋ ์์ค์ด์ผ. ๐
๐ฆ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ตฌ์กฐ ์ค๊ณ
// regex_engine.h - ํค๋ ํ์ผ
#ifndef REGEX_ENGINE_H
#define REGEX_ENGINE_H
#include <stddef.h>
// ์ปดํ์ผ ์ต์
ํ๋๊ทธ
#define REGEX_CASELESS 0x01 // ๋์๋ฌธ์ ๋ฌด์
#define REGEX_MULTILINE 0x02 // ๋ฉํฐ๋ผ์ธ ๋ชจ๋
#define REGEX_DOTALL 0x04 // . ์ด ๊ฐํ ๋ฌธ์๋ ๋งค์นญ
// ์๋ฌ ์ฝ๋
#define REGEX_OK 0
#define REGEX_NOMATCH 1
#define REGEX_BADPATTERN 2
#define REGEX_NOMEM 3
// ๋งค์นญ ๊ฒฐ๊ณผ
typedef struct {
size_t start;
size_t end;
} regex_match_t;
// ์ปดํ์ผ๋ ์ ๊ทํํ์
typedef struct regex_s regex_t;
// API ํจ์๋ค
regex_t* regex_compile(const char *pattern, int flags);
void regex_free(regex_t *re);
int regex_match(regex_t *re, const char *text, regex_match_t *match);
int regex_search(regex_t *re, const char *text, regex_match_t *match);
char* regex_replace(regex_t *re, const char *text, const char *replacement);
const char* regex_error(int errcode);
#endif // REGEX_ENGINE_H
๐ง ๊ตฌํ ํ์ผ
// regex_engine.c - ๊ตฌํ ํ์ผ
#include "regex_engine.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// ๋ด๋ถ ๊ตฌ์กฐ์ฒด
struct regex_s {
char *pattern;
int flags;
// ... ๋ด๋ถ ์ํ ๋จธ์ ๋ฐ์ดํฐ
};
// ํจํด ์ปดํ์ผ
regex_t* regex_compile(const char *pattern, int flags) {
if (!pattern)
return NULL;
regex_t *re = malloc(sizeof(regex_t));
if (!re)
return NULL;
re->pattern = strdup(pattern);
re->flags = flags;
// ํจํด ํ์ฑ ๋ฐ ์ปดํ์ผ
// (์ค์ ๊ตฌํ ์๋ต)
return re;
}
// ๋ฉ๋ชจ๋ฆฌ ํด์
void regex_free(regex_t *re) {
if (re) {
free(re->pattern);
free(re);
}
}
// ์ ์ฒด ๋ฌธ์์ด ๋งค์นญ
int regex_match(regex_t *re, const char *text, regex_match_t *match) {
if (!re || !text)
return REGEX_BADPATTERN;
// ๋งค์นญ ๋ก์ง ๊ตฌํ
// (์ค์ ๊ตฌํ ์๋ต)
if (match) {
match->start = 0;
match->end = strlen(text);
}
return REGEX_OK;
}
// ๋ถ๋ถ ๋ฌธ์์ด ๊ฒ์
int regex_search(regex_t *re, const char *text, regex_match_t *match) {
if (!re || !text)
return REGEX_BADPATTERN;
// ๊ฒ์ ๋ก์ง ๊ตฌํ
// (์ค์ ๊ตฌํ ์๋ต)
return REGEX_NOMATCH;
}
// ํจํด ์นํ
char* regex_replace(regex_t *re, const char *text, const char *replacement) {
if (!re || !text || !replacement)
return NULL;
// ์นํ ๋ก์ง ๊ตฌํ
// (์ค์ ๊ตฌํ ์๋ต)
return strdup(text);
}
// ์๋ฌ ๋ฉ์์ง
const char* regex_error(int errcode) {
switch (errcode) {
case REGEX_OK:
return "์ฑ๊ณต";
case REGEX_NOMATCH:
return "๋งค์นญ๋์ง ์์";
case REGEX_BADPATTERN:
return "์๋ชป๋ ํจํด";
case REGEX_NOMEM:
return "๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ";
default:
return "์ ์ ์๋ ์๋ฌ";
}
}
๐ฎ ์ฌ์ฉ ์์
// example.c - ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ ์์
#include "regex_engine.h"
#include <stdio.h>
int main() {
// 1. ์ด๋ฉ์ผ ๊ฒ์ฆ
regex_t *email_re = regex_compile(
"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}",
0
);
if (email_re) {
regex_match_t match;
int result = regex_match(email_re, "user@example.com", &match);
if (result == REGEX_OK) {
printf("โ ์ ํจํ ์ด๋ฉ์ผ์
๋๋ค!\n");
} else {
printf("โ ์ ํจํ์ง ์์ ์ด๋ฉ์ผ์
๋๋ค.\n");
}
regex_free(email_re);
}
// 2. URL์์ ๋๋ฉ์ธ ์ถ์ถ
regex_t *url_re = regex_compile(
"https?://([a-zA-Z0-9.-]+)",
0
);
if (url_re) {
regex_match_t match;
const char *url = "https://www.jaenung.net/path";
if (regex_search(url_re, url, &match) == REGEX_OK) {
printf("๋๋ฉ์ธ: %.*s\n",
(int)(match.end - match.start),
url + match.start);
}
regex_free(url_re);
}
// 3. ์ ํ๋ฒํธ ๋ง์คํน
regex_t *phone_re = regex_compile(
"[0-9]{3}-[0-9]{4}-[0-9]{4}",
0
);
if (phone_re) {
char *text = "์ฐ๋ฝ์ฒ: 010-1234-5678";
char *masked = regex_replace(phone_re, text, "***-****-****");
printf("์๋ณธ: %s\n", text);
printf("๋ง์คํน: %s\n", masked);
free(masked);
regex_free(phone_re);
}
return 0;
}
๐ ๊ณ ๊ธ ์ฃผ์ : ๋ ๊น์ด ํ๊ณ ๋ค๊ธฐ
๊ธฐ๋ณธ์ ๋์ด์ ์ ๋ง ๊ฐ๋ ฅํ ์์ง์ ๋ง๋ค๊ณ ์ถ๋ค๋ฉด, ์ด๋ฐ ๊ณ ๊ธ ์ฃผ์ ๋ค๋ ์์๋ฌ์ผ ํด! ๐
๐ง 1. Thompson์ NFA ๊ตฌ์ฑ ์๊ณ ๋ฆฌ์ฆ
Ken Thompson์ด ๊ฐ๋ฐํ ์ด ์๊ณ ๋ฆฌ์ฆ์ ์ ๊ทํํ์์ NFA๋ก ๋ณํํ๋ ๊ฐ์ฅ ์ฐ์ํ ๋ฐฉ๋ฒ์ด์ผ. ํต์ฌ ์์ด๋์ด๋ ๊ฐ ์ ๊ทํํ์ ์ฐ์ฐ์๋ฅผ ์์ NFA ์กฐ๊ฐ์ผ๋ก ๋ง๋ค๊ณ , ์ด๋ค์ ์กฐํฉํ๋ ๊ฑฐ์ง.
// NFA ์ํ ๊ตฌ์กฐ์ฒด
typedef struct State {
int c; // ๋งค์นญํ ๋ฌธ์ (ํน์๊ฐ: SPLIT, MATCH)
struct State *out; // ๋ค์ ์ํ
struct State *out1; // ๋ถ๊ธฐ ์ํ (SPLIT์ฉ)
int lastlist; // ๋ฐฉ๋ฌธ ํ์์ฉ
} State;
#define SPLIT 256
#define MATCH 257
// NFA ํ๋๊ทธ๋จผํธ
typedef struct Frag {
State *start;
State **out; // ์์ง ์ฐ๊ฒฐ ์ ๋ ์ถ๋ ฅ๋ค
} Frag;
// ๊ธฐ๋ณธ ๋ฌธ์ NFA ์์ฑ
Frag char_frag(int c) {
State *s = new_state(c, NULL, NULL);
Frag f = {s, &s->out};
return f;
}
// ์ฐ๊ฒฐ (concatenation)
Frag cat_frag(Frag f1, Frag f2) {
patch(f1.out, f2.start); // f1์ ์ถ๋ ฅ์ f2์ ์์์ ์ฐ๊ฒฐ
return (Frag){f1.start, f2.out};
}
// ์ ํ (alternation) - |
Frag alt_frag(Frag f1, Frag f2) {
State *s = new_state(SPLIT, f1.start, f2.start);
return (Frag){s, append(f1.out, f2.out)};
}
// ๋ฐ๋ณต (repetition) - *
Frag star_frag(Frag f) {
State *s = new_state(SPLIT, f.start, NULL);
patch(f.out, s); // ์ถ๋ ฅ์ ๋ค์ ์์์ผ๋ก
return (Frag){s, &s->out1};
}
์ด ๋ฐฉ์์ ์ฅ์ ์ ํจํด์ ํ ๋ฒ๋ง ์ค์บํ๋ฉด์ NFA๋ฅผ ๋ง๋ค ์ ์๋ค๋ ๊ฑฐ์ผ! ์๊ฐ ๋ณต์ก๋๊ฐ O(n)์ด์ง. ๐
๐ 2. NFA์์ DFA๋ก ๋ณํ (Subset Construction)
NFA๋ ๊ตฌํ์ด ์ฝ์ง๋ง ๋๋ฆด ์ ์์ด. DFA๋ก ๋ณํํ๋ฉด ๋งค์นญ ์๋๊ฐ ํจ์ฌ ๋นจ๋ผ์ ธ!
// DFA ์ํ
typedef struct DFAState {
int *nfa_states; // ํฌํจ๋ NFA ์ํ๋ค
int num_states;
struct DFAState *next[256]; // ๊ฐ ๋ฌธ์์ ๋ํ ์ ์ด
int is_match; // ๋งค์นญ ์ํ ์ฌ๋ถ
} DFAState;
// epsilon ํด๋ก์ ๊ณ์ฐ
void epsilon_closure(int *nfa_states, int *result, int *result_size) {
// NFA ์ํ๋ค์์ epsilon ์ ์ด๋ก ๋๋ฌ ๊ฐ๋ฅํ ๋ชจ๋ ์ํ ์ฐพ๊ธฐ
// (๊ตฌํ ์๋ต)
}
// NFA๋ฅผ DFA๋ก ๋ณํ
DFAState* nfa_to_dfa(State *start) {
DFAState *dfa_start = malloc(sizeof(DFAState));
// ์์ ์ํ์ epsilon ํด๋ก์ ๊ณ์ฐ
epsilon_closure(&start, dfa_start->nfa_states, &dfa_start->num_states);
// BFS๋ก ๋ชจ๋ DFA ์ํ ์์ฑ
Queue *q = queue_create();
queue_push(q, dfa_start);
while (!queue_empty(q)) {
DFAState *current = queue_pop(q);
// ๊ฐ ์
๋ ฅ ๋ฌธ์์ ๋ํด
for (int c = 0; c < 256; c++) {
// ์ ์ด ๊ฐ๋ฅํ NFA ์ํ๋ค ์ฐพ๊ธฐ
int *next_nfa = find_transitions(current->nfa_states, c);
if (next_nfa) {
// ์ DFA ์ํ ์์ฑ ๋๋ ๊ธฐ์กด ์ํ ์ฌ์ฌ์ฉ
DFAState *next_dfa = get_or_create_dfa_state(next_nfa);
current->next[c] = next_dfa;
if (!next_dfa->visited) {
queue_push(q, next_dfa);
next_dfa->visited = 1;
}
}
}
}
return dfa_start;
}
DFA๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ ์ฐ์ง๋ง, ๋งค์นญ ์๋๊ฐ ์ ๋ง ๋นจ๋ผ! ํนํ ๊ฐ์ ํจํด์ ์ฌ๋ฌ ๋ฒ ์ฌ์ฉํ ๋ ํจ๊ณผ์ ์ด์ผ. ๐จ
๐ฏ 3. ๋ฐฑ๋ ํผ๋ฐ์ค์ ์บก์ฒ ๊ทธ๋ฃน
์ง์ง ๊ฐ๋ ฅํ ๊ธฐ๋ฅ! ๋งค์นญ๋ ๋ถ๋ถ์ ๋์ค์ ๋ค์ ์ฐธ์กฐํ ์ ์์ด.
// ์บก์ฒ ๊ทธ๋ฃน ์ ๋ณด
typedef struct {
int group_num;
char *start;
char *end;
} CaptureGroup;
// ๋ฐฑ๋ ํผ๋ฐ์ค๋ฅผ ์ง์ํ๋ ๋งค์นญ
int match_with_backrefs(char *regexp, char *text, CaptureGroup *groups, int *group_count) {
// (pattern) - ์บก์ฒ ๊ทธ๋ฃน
if (*regexp == '(') {
char *group_start = text;
char *pattern_end = find_matching_paren(regexp);
// ๊ทธ๋ฃน ๋ด๋ถ ํจํด ๋งค์นญ
if (match_with_backrefs(regexp + 1, text, groups, group_count)) {
// ์บก์ฒ ์ ์ฅ
groups[*group_count].group_num = *group_count + 1;
groups[*group_count].start = group_start;
groups[*group_count].end = text;
(*group_count)++;
// ๊ทธ๋ฃน ์ดํ ๊ณ์
return match_with_backrefs(pattern_end + 1, text, groups, group_count);
}
return 0;
}
// \1, \2 ๋ฑ - ๋ฐฑ๋ ํผ๋ฐ์ค
if (*regexp == '\\' && isdigit(regexp[1])) {
int group_num = regexp[1] - '0';
if (group_num <= *group_count) {
CaptureGroup *ref = &groups[group_num - 1];
int len = ref->end - ref->start;
// ์บก์ฒ๋ ๋ด์ฉ๊ณผ ๋์ผํ์ง ํ์ธ
if (strncmp(text, ref->start, len) == 0) {
return match_with_backrefs(regexp + 2, text + len, groups, group_count);
}
}
return 0;
}
// ์ผ๋ฐ ๋งค์นญ ๊ณ์...
}
// ์ฌ์ฉ ์: ์ค๋ณต ๋จ์ด ์ฐพ๊ธฐ
// ํจํด: \b(\w+)\s+\1\b
// "the the" ๊ฐ์ ์ค๋ณต ๋จ์ด๋ฅผ ์ฐพ์ ์ ์์ด!
๐ ํ์ต ๋ฆฌ์์ค์ ๋ค์ ๋จ๊ณ
์ ๊ทํํ์ ์์ง ๊ฐ๋ฐ์ ์ ๋ง ๊น์ด ์๋ ์ฃผ์ ์ผ. ์ฌ๊ธฐ์ ๋ฐฐ์ด ๊ฑด ์์์ผ ๋ฟ์ด๊ณ , ๋ ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด ์ด๋ฐ ์๋ฃ๋ค์ ์ถ์ฒํด! ๐
๐ ์ถ์ฒ ํ์ต ์๋ฃ
1. ํ์ ๋
ผ๋ฌธ๋ค
โข Ken Thompson (1968) - "Regular Expression Search Algorithm"
โข Russ Cox - "Regular Expression Matching Can Be Simple And Fast"
โข Alfred Aho - "Compilers: Principles, Techniques, and Tools" (๋๋๊ณค ๋ถ)
2. ์คํ์์ค ํ๋ก์ ํธ
โข RE2 (Google) - C++๋ก ์์ฑ๋ ๋น ๋ฅด๊ณ ์์ ํ ์ ๊ทํํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
โข PCRE - ๊ฐ์ฅ ๋๋ฆฌ ์ฐ์ด๋ ์ ๊ทํํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
โข TRE - ๊ทผ์ฌ ๋งค์นญ์ ์ง์ํ๋ ์ ๊ทํํ์ ์์ง
3. ์จ๋ผ์ธ ๋๊ตฌ
โข regex101.com - ์ ๊ทํํ์ ํ
์คํธ ๋ฐ ๋๋ฒ๊น
โข regexr.com - ์๊ฐ์ ์ ๊ทํํ์ ํ์ต
โข debuggex.com - ์ ๊ทํํ์ ์๊ฐํ
๐ก ์ค๋ ฅ ํฅ์ ํ
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์ ๊ทํํ์ ๊ด๋ จ ํ๋ก์ ํธ๋ฅผ ์ฐพ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ! ์ค์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ฉด์ ๋ฐฐ์ฐ๋ ๊ฒ ๊ฐ์ฅ ํจ๊ณผ์ ์ด๊ฑฐ๋ . ํ
์คํธ ์ฒ๋ฆฌ, ๋ฐ์ดํฐ ์ถ์ถ, ๋ก๊ทธ ๋ถ์ ๊ฐ์ ํ๋ก์ ํธ๋ค์ด ๋ง์. ๐
๋ํ ๋ค๋ฅธ ์ฌ๋๋ค์ ์ฝ๋๋ฅผ ์ฝ์ด๋ณด๋ ๊ฒ๋ ์ ๋ง ์ค์ํด. GitHub์์ "regex engine C"๋ก ๊ฒ์ํ๋ฉด ๋ค์ํ ๊ตฌํ์ ๋ณผ ์ ์์ด!
๐ฏ ์ค์ ํ๋ก์ ํธ ์์ด๋์ด
์ด์ ๋ฐฐ์ด ๊ฑธ ํ์ฉํด์ ๋ญ๊ฐ ๋ง๋ค์ด๋ณผ ์๊ฐ์ด์ผ! ๋ช ๊ฐ์ง ์ฌ๋ฏธ์๋ ํ๋ก์ ํธ ์์ด๋์ด๋ฅผ ์๊ฐํ ๊ฒ. ๐ก
๐ ํ๋ก์ ํธ 1: ์ฝ๋ ๊ฒ์ ๋๊ตฌ
์์ค ์ฝ๋์์ ํน์ ํจํด์ ์ฐพ๋ ๋๊ตฌ๋ฅผ ๋ง๋ค์ด๋ด. grep ๊ฐ์ ๊ฑฐ์ง๋ง ๋ ๋๋ํ๊ฒ!
// ๊ธฐ๋ฅ ์์ด๋์ด:
// - ํจ์ ์ ์ ์ฐพ๊ธฐ
// - TODO ์ฃผ์ ์ถ์ถ
// - ์ฌ์ฉ๋์ง ์๋ ๋ณ์ ์ฐพ๊ธฐ
// - ์ฝ๋ฉ ์คํ์ผ ๊ฒ์ฌ
void search_code_patterns(char *directory, char *pattern) {
// ๋๋ ํ ๋ฆฌ ์ํ
// ๊ฐ ํ์ผ์์ ํจํด ๊ฒ์
// ๊ฒฐ๊ณผ๋ฅผ ํ์ผ๋ช
:๋ผ์ธ๋ฒํธ:๋ด์ฉ ํ์์ผ๋ก ์ถ๋ ฅ
}
๐ ํ๋ก์ ํธ 2: ๋ก๊ทธ ๋ถ์๊ธฐ
์๋ฒ ๋ก๊ทธ๋ฅผ ๋ถ์ํด์ ์ ์ฉํ ์ ๋ณด๋ฅผ ์ถ์ถํ๋ ๋๊ตฌ!
// ๊ธฐ๋ฅ ์์ด๋์ด:
// - ์๋ฌ ํจํด ๊ฐ์ง
// - ์ ์ ํต๊ณ ์์ฑ
// - ์ด์ ํจํด ์๋ฆผ
// - ์ฑ๋ฅ ๋ณ๋ชฉ ์ง์ ์ฐพ๊ธฐ
typedef struct {
int total_requests;
int error_count;
int unique_ips;
double avg_response_time;
} LogStats;
LogStats analyze_logs(char *logfile, char **patterns, int pattern_count);
โ๏ธ ํ๋ก์ ํธ 3: ์ด๋ฉ์ผ ํํฐ
์คํธ ๋ฉ์ผ์ ๊ฑธ๋ฌ๋ด๊ฑฐ๋ ์ค์ํ ๋ฉ์ผ์ ๋ถ๋ฅํ๋ ์์คํ !
// ๊ธฐ๋ฅ ์์ด๋์ด:
// - ์คํธ ํจํด ๊ฐ์ง
// - ๋ฐ์ ์ ํ์ดํธ๋ฆฌ์คํธ/๋ธ๋๋ฆฌ์คํธ
// - ํค์๋ ๊ธฐ๋ฐ ์๋ ๋ถ๋ฅ
// - ์ฐ์ ์์ ์๋ ์ค์
typedef enum {
CATEGORY_SPAM,
CATEGORY_IMPORTANT,
CATEGORY_NEWSLETTER,
CATEGORY_PERSONAL,
CATEGORY_WORK
} EmailCategory;
EmailCategory classify_email(char *subject, char *body, char *sender);
๐ ํ๋ก์ ํธ 4: ๋ฐ์ดํฐ ๋ง์คํน ๋๊ตฌ
๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ์๋์ผ๋ก ๋ง์คํนํ๋ ๋๊ตฌ!
// ๊ธฐ๋ฅ ์์ด๋์ด:
// - ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ ๋ง์คํน
// - ์ ์ฉ์นด๋ ๋ฒํธ ๋ง์คํน
// - ์ด๋ฉ์ผ ์ฃผ์ ๋ถ๋ถ ๋ง์คํน
// - ์ ํ๋ฒํธ ๋ง์คํน
char* mask_sensitive_data(char *text, int mask_level) {
// mask_level์ ๋ฐ๋ผ ๋ง์คํน ๊ฐ๋ ์กฐ์
// 0: ๋ถ๋ถ ๋ง์คํน (์: 010-****-5678)
// 1: ์์ ๋ง์คํน (์: ***-****-****)
// 2: ํด์๋ก ๋์ฒด
}
โ ๏ธ ์ฃผ์์ฌํญ๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค
์ ๊ทํํ์ ์์ง์ ๋ง๋ค ๋ ๊ผญ ์์์ผ ํ ์ฃผ์์ฌํญ๋ค์ด์ผ. ์ด๊ฑฐ ๋ชจ๋ฅด๋ฉด ๋์ค์ ํฐ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์ด! ๐จ
๐ ์ฑ๋ฅ ํจ์ ์ฃผ์!
1. ์ฌ์์ ๋ฐฑํธ๋ํน (Catastrophic Backtracking)
ํน์ ํจํด์ ์
๋ ฅ ๊ธธ์ด์ ๋ฐ๋ผ ์ง์์ ์ผ๋ก ๋๋ ค์ง ์ ์์ด!
// ์ํํ ํจํด ์์
"(a+)+b" // ์
๋ ฅ "aaaaaaaaac"์์ ๋งค์ฐ ๋๋ฆผ
"(a*)*b" // ๋ง์ฐฌ๊ฐ์ง๋ก ์ํ
"(a|a)*b" // ์ค๋ณต๋ ์ ํ์ง
// ํด๊ฒฐ ๋ฐฉ๋ฒ:
// 1. ์์ ์๋์ ์ฌ์ฉ (a++)
// 2. ์์ ๊ทธ๋ฃน ์ฌ์ฉ (?>a+)
// 3. ํจํด ๋จ์ํ
"a+b" // ํจ์ฌ ์์ ํ๊ณ ๋น ๋ฆ
๐ ๋ณด์ ๊ณ ๋ ค์ฌํญ
1. ReDoS (Regular Expression Denial of Service)
์
์์ ์ธ ์
๋ ฅ์ผ๋ก ์๋ฒ๋ฅผ ๋ง๋น์ํฌ ์ ์์ด!
// ๋ณด์ ๋์ฑ
#define MAX_PATTERN_LENGTH 1000
#define MAX_TEXT_LENGTH 100000
#define MAX_EXECUTION_TIME 1000 // ๋ฐ๋ฆฌ์ด
int safe_match(char *pattern, char *text) {
// ๊ธธ์ด ๊ฒ์ฆ
if (strlen(pattern) > MAX_PATTERN_LENGTH ||
strlen(text) > MAX_TEXT_LENGTH) {
return REGEX_ERROR_TOO_LONG;
}
// ํ์์์ ์ค์
clock_t start = clock();
int result = match(pattern, text);
clock_t end = clock();
double elapsed = (double)(end - start) / CLOCKS_PER_SEC * 1000;
if (elapsed > MAX_EXECUTION_TIME) {
return REGEX_ERROR_TIMEOUT;
}
return result;
}
๐พ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
// ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง
typedef struct {
void **allocations;
int count;
int capacity;
} MemoryTracker;
MemoryTracker* tracker_create() {
MemoryTracker *t = malloc(sizeof(MemoryTracker));
t->allocations = malloc(sizeof(void*) * 100);
t->count = 0;
t->capacity = 100;
return t;
}
void* tracked_malloc(MemoryTracker *t, size_t size) {
void *ptr = malloc(size);
if (ptr) {
if (t->count >= t->capacity) {
t->capacity *= 2;
t->allocations = realloc(t->allocations,
sizeof(void*) * t->capacity);
}
t->allocations[t->count++] = ptr;
}
return ptr;
}
void tracker_free_all(MemoryTracker *t) {
for (int i = 0; i < t->count; i++) {
free(t->allocations[i]);
}
free(t->allocations);
free(t);
}
๐ ๋ง๋ฌด๋ฆฌ: ๋น์ ๋ง์ ์์ง์ ๋ง๋ค์ด๋ณด์ธ์!
์! ์ฌ๊ธฐ๊น์ง ์ ๋ง ๊ธด ์ฌ์ ์ด์์ด. ์ ๊ทํํ์ ์์ง์ ๋ง๋๋ ๊ฑด ์ฝ์ง ์์ง๋ง, ์ ๋ง ๋ณด๋์ฐฌ ๊ฒฝํ์ด์ผ. ๐
๐ฏ ํต์ฌ ์์ฝ
1. ๊ธฐ๋ณธ ์๋ฆฌ
โข ์ ๊ทํํ์์ ํจํด์ ํํํ๋ ์ธ์ด
โข NFA์ DFA ๋ ๊ฐ์ง ๊ตฌํ ๋ฐฉ์
โข ์ฌ๊ท์ ๋ฐฑํธ๋ํน์ด ํต์ฌ ๋ฉ์ปค๋์ฆ
2. ๊ตฌํ ๋จ๊ณ
โข ๊ฐ๋จํ ๋ฌธ์ ๋งค์นญ๋ถํฐ ์์
โข ๋ฉํ๋ฌธ์ ํ๋์ฉ ์ถ๊ฐ
โข ์ต์ ํ๋ ๋์ค์
3. ์ค์ ํ์ฉ
โข ํ
์คํธ ๊ฒ์ ๋ฐ ์ถ์ถ
โข ๋ฐ์ดํฐ ๊ฒ์ฆ
โข ๋ก๊ทธ ๋ถ์
โข ๋ณด์ ํํฐ๋ง
4. ์ฃผ์์ฌํญ
โข ์ฑ๋ฅ ํจ์ ์กฐ์ฌ
โข ๋ณด์ ๊ณ ๋ ค
โข ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ์ฒ ์ ํ
๐ ๋ค์ ๋จ๊ณ๋ก!
์ด์ ๋น์ ์ฐจ๋ก์ผ! ์์ ํ๋ก์ ํธ๋ถํฐ ์์ํด์ ์ ์ ๊ธฐ๋ฅ์ ์ถ๊ฐํด๋ด. ์๋ฒฝํ ํ์๋ ์์ด. ์ค์ํ ๊ฑด ์ง์ ๋ง๋ค์ด๋ณด๋ฉด์ ๋ฐฐ์ฐ๋ ๊ฑฐ์ผ.
๋งํ๋ ๋ถ๋ถ์ด ์๋ค๋ฉด ์ฌ๋ฅ๋ท์์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ์ํตํด๋ด. ๊ฒฝํ ๋ง์ ์ ๋ฐฐ ๊ฐ๋ฐ์๋ค์ ์กฐ์ธ์ ๋ฃ๊ฑฐ๋, ์ง์ ๋ฉํ ๋ง์ ๋ฐ์ ์๋ ์์ด. ํจ๊ป ๋ฐฐ์ฐ๊ณ ์ฑ์ฅํ๋ ๊ฒ ํ๋ก๊ทธ๋๋ฐ์ ์ง์ง ์ฌ๋ฏธ๊ฑฐ๋ ! ๐
์ ๊ทํํ์ ์์ง ๊ฐ๋ฐ์ ์ปดํจํฐ ๊ณผํ์ ์ฌ๋ฌ ๋ถ์ผ๋ฅผ ์์ฐ๋ฅด๋ ์ข
ํฉ ์์ ์ด์ผ. ์คํ ๋งํ ์ด๋ก , ์ปดํ์ผ๋ฌ, ์๊ณ ๋ฆฌ์ฆ, ์๋ฃ๊ตฌ์กฐ... ์ด ๋ชจ๋ ๊ฑธ ์ค์ ์์ ๋ฐฐ์ธ ์ ์์ด. ๋์ ํด๋ด! ๐ช
Happy Coding! ๐
์ ๊ทํํ์ ์์ง ๊ฐ๋ฐ์ ์ธ๊ณ์ ์ค์ ๊ฒ์ ํ์ํฉ๋๋ค!
๊ด๋ จ ํค์๋
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.

๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.