์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ” C์–ธ์–ด๋กœ ๋งŒ๋“œ๋Š” ์ •๊ทœํ‘œํ˜„์‹ ํŒจํ„ด ๋งค์นญ ์—”์ง„: ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ์˜ ๋งˆ๋ฒ•์‚ฌ ๋˜๊ธฐ

๐Ÿ” C์–ธ์–ด๋กœ ๋งŒ๋“œ๋Š” ์ •๊ทœํ‘œํ˜„์‹ ํŒจํ„ด ๋งค์นญ ์—”์ง„: ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ์˜ ๋งˆ๋ฒ•์‚ฌ ๋˜๊ธฐ

๋ฌธ์ž์—ด ์†์—์„œ ์›ํ•˜๋Š” ํŒจํ„ด์„ ์ฐพ์•„๋‚ด๋Š” ๊ฐ•๋ ฅํ•œ ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ ์—”์ง„ ๊ฐœ๋ฐœ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๐Ÿ’ฌ ์•ˆ๋…•! ์˜ค๋Š˜์€ ์ •๋ง ์žฌ๋ฏธ์žˆ๋Š” ์ฃผ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  ์™”์–ด. C์–ธ์–ด๋กœ ์ •๊ทœํ‘œํ˜„์‹ ํŒจํ„ด ๋งค์นญ ์—”์ง„์„ ์ง์ ‘ ๋งŒ๋“œ๋Š” ๊ฑฐ์•ผ! "๋ญ? ๊ทธ๊ฒŒ ๊ฐ€๋Šฅํ•ด?"๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, ์ƒ๊ฐ๋ณด๋‹ค ์›๋ฆฌ๋Š” ๋‹จ์ˆœํ•˜๋ฉด์„œ๋„ ์—„์ฒญ๋‚˜๊ฒŒ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด. ๐Ÿ˜Ž

์ •๊ทœํ‘œํ˜„์‹(Regular Expression)์€ ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ์˜ ํ•ต์‹ฌ ๋„๊ตฌ์ง€. ์ด๋ฉ”์ผ ๊ฒ€์ฆ๋ถ€ํ„ฐ ๋กœ๊ทธ ํŒŒ์ผ ๋ถ„์„, ๋ฐ์ดํ„ฐ ์ถ”์ถœ๊นŒ์ง€ ์ •๋ง ๋‹ค์–‘ํ•œ ๊ณณ์—์„œ ์“ฐ์ด๊ฑฐ๋“ . ๊ทธ๋Ÿฐ๋ฐ ์ด๊ฑธ C์–ธ์–ด๋กœ ์ง์ ‘ ๊ตฌํ˜„ํ•œ๋‹ค? ์™„์ „ ๋ฉ‹์ง„ ์ผ์ด์•ผ! ๐Ÿš€

๐ŸŽฏ ์ •๊ทœํ‘œํ˜„์‹ ์—”์ง„, ์™œ ์ง์ ‘ ๋งŒ๋“ค์–ด์•ผ ํ• ๊นŒ?

๐Ÿค” "๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์“ฐ๋ฉด ๋˜๋Š” ๊ฑฐ ์•„๋ƒ?"

๋งž์•„, PCRE๋‚˜ POSIX regex ๊ฐ™์€ ํ›Œ๋ฅญํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ์žˆ์–ด. ํ•˜์ง€๋งŒ ์ง์ ‘ ๋งŒ๋“ค์–ด๋ณด๋ฉด ์–ป๋Š” ๊ฒŒ ์ •๋ง ๋งŽ์•„:


โœ… ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ดํ•ด๋„ ํ–ฅ์ƒ - ์˜คํ† ๋งˆํƒ€ ์ด๋ก ๊ณผ ์ปดํŒŒ์ผ๋Ÿฌ ์›๋ฆฌ๋ฅผ ์‹ค์ „์œผ๋กœ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์–ด
โœ… ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ์ž์œ ๋„ - ํŠน์ • ๋„๋ฉ”์ธ์— ์ตœ์ ํ™”๋œ ํŒจํ„ด ๋งค์นญ ๊ฐ€๋Šฅ
โœ… ์„ฑ๋Šฅ ์ตœ์ ํ™” - ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋งŒ ๊ตฌํ˜„ํ•ด์„œ ๊ฐ€๋ณ๊ณ  ๋น ๋ฅด๊ฒŒ
โœ… ์ž„๋ฒ ๋””๋“œ ํ™˜๊ฒฝ - ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜์กด์„ฑ ์—†์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅ
โœ… ํ•™์Šต ํšจ๊ณผ - ๋ฌธ์ž์—ด ์ฒ˜๋ฆฌ์™€ ์ƒํƒœ ๋จธ์‹ ์— ๋Œ€ํ•œ ๊นŠ์€ ์ดํ•ด

ํŠนํžˆ ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ๋ฐฐ์šธ ๋•Œ, ์ด๋Ÿฐ ์ €์ˆ˜์ค€ ๊ตฌํ˜„ ๊ฒฝํ—˜์€ ์ •๋ง ํฐ ์ž์‚ฐ์ด ๋ผ. ๋‹จ์ˆœํžˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๋Š” ๊ฒƒ๊ณผ ๋‚ด๋ถ€ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฑด ์™„์ „ํžˆ ๋‹ค๋ฅธ ์ฐจ์›์ด๊ฑฐ๋“ ! ๐Ÿ’ก

์ •๊ทœํ‘œํ˜„์‹ ์—”์ง„ ์ฒ˜๋ฆฌ ํ๋ฆ„ ์ž…๋ ฅ ํŒจํ„ด "a.*b+" ํŒŒ์‹ฑ ๊ตฌ๋ฌธ ํŠธ๋ฆฌ AST ์ƒ์„ฑ ์ปดํŒŒ์ผ NFA/DFA ์ƒํƒœ ๋จธ์‹  ๋งค์นญ ์ž…๋ ฅ ํ…์ŠคํŠธ "axxxbbb" ๋งค์นญ ๊ฒฐ๊ณผ โœ“ ์„ฑ๊ณต ๐Ÿ“ ํŒจํ„ด ์ •์˜ โš™๏ธ ์—”์ง„ ์ฒ˜๋ฆฌ โœจ ๊ฒฐ๊ณผ ์ถœ๋ ฅ

๐Ÿงฉ ์ •๊ทœํ‘œํ˜„์‹ ๊ธฐ์ดˆ: ํŒจํ„ด์˜ ์–ธ์–ด

๋ณธ๊ฒฉ์ ์œผ๋กœ ์—”์ง„์„ ๋งŒ๋“ค๊ธฐ ์ „์—, ์ •๊ทœํ‘œํ˜„์‹์˜ ๊ธฐ๋ณธ ๋ฌธ๋ฒ•๋ถ€ํ„ฐ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž. ์ด๊ฒŒ ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•ด์•ผ ํ•  ๊ธฐ๋Šฅ๋“ค์ด์•ผ! ๐Ÿ“š

๐Ÿ”ค ๊ธฐ๋ณธ ๋ฉ”ํƒ€ ๋ฌธ์ž๋“ค


๋ฉ”ํƒ€๋ฌธ์ž ์˜๋ฏธ ์˜ˆ์‹œ
. ์ž„์˜์˜ ํ•œ ๋ฌธ์ž "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);
}
์„ฑ๋Šฅ ์ตœ์ ํ™” ํšจ๊ณผ ๋น„๊ต ๊ธฐ๋ณธ ๊ตฌํ˜„ 100ms ํŒจํ„ด ์ปดํŒŒ์ผ 70ms (-30%) ๋ฉ”๋ชจ์ด์ œ์ด์…˜ 50ms (-50%) ์กฐ๊ธฐ ์ข…๋ฃŒ 30ms (-70%) ๋ชจ๋“  ์ตœ์ ํ™” 20ms (-80%) ์ตœ์ ํ™” ์ „๋žต๋ณ„ ๊ฐœ์„  ํšจ๊ณผ ์ตœ์ ํ™” ์ ์šฉ ๊ธฐ๋ณธ ๊ตฌํ˜„ โ€ป ์‹ค์ œ ์„ฑ๋Šฅ์€ ํŒจํ„ด๊ณผ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

๐Ÿ› ๏ธ ์‹ค์ „ ํ™œ์šฉ: ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ ์—”์ง„ ๋งŒ๋“ค๊ธฐ

์ด์ œ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์ •๊ทœํ‘œํ˜„์‹ ์—”์ง„์„ ์‹ค์ œ๋กœ ํ™œ์šฉํ•ด๋ณด์ž! ๋ช‡ ๊ฐ€์ง€ ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณผ๊ฒŒ. ๐Ÿ’ผ

๐Ÿ“ง ์˜ˆ์ œ 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" ๊ฐ™์€ ์ค‘๋ณต ๋‹จ์–ด๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์–ด!
NFA vs DFA ๋น„๊ต NFA (๋น„๊ฒฐ์ •์ ) DFA (๊ฒฐ์ •์ ) S0 S1 S2 S3 a ฮต b c D0 D1 D2 a c NFA ํŠน์ง• โœ“ ์—ฌ๋Ÿฌ ์ƒํƒœ๋กœ ๋™์‹œ ์ „์ด ๊ฐ€๋Šฅ โœ“ ฮต(์—ก์‹ค๋ก ) ์ „์ด ํ—ˆ์šฉ โœ“ ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•˜๊ณ  ์ง๊ด€์  โœ“ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์  โœ— ๋ฐฑํŠธ๋ž˜ํ‚น์œผ๋กœ ๋А๋ฆด ์ˆ˜ ์žˆ์Œ โœ— ์ตœ์•…์˜ ๊ฒฝ์šฐ ์ง€์ˆ˜ ์‹œ๊ฐ„ DFA ํŠน์ง• โœ“ ๊ฐ ์ž…๋ ฅ๋งˆ๋‹ค ์ •ํ™•ํžˆ ํ•˜๋‚˜์˜ ์ „์ด โœ“ ๋ฐฑํŠธ๋ž˜ํ‚น ๋ถˆํ•„์š” โœ“ ์„ ํ˜• ์‹œ๊ฐ„ O(n) ๋ณด์žฅ โœ“ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์„ฑ๋Šฅ โœ— ์ƒํƒœ ํญ๋ฐœ ๋ฌธ์ œ โœ— ๊ตฌํ˜„ ๋ณต์žก๋„ ๋†’์Œ

๐ŸŽ“ ํ•™์Šต ๋ฆฌ์†Œ์Šค์™€ ๋‹ค์Œ ๋‹จ๊ณ„

์ •๊ทœํ‘œํ˜„์‹ ์—”์ง„ ๊ฐœ๋ฐœ์€ ์ •๋ง ๊นŠ์ด ์žˆ๋Š” ์ฃผ์ œ์•ผ. ์—ฌ๊ธฐ์„œ ๋ฐฐ์šด ๊ฑด ์‹œ์ž‘์ผ ๋ฟ์ด๊ณ , ๋” ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด๋Ÿฐ ์ž๋ฃŒ๋“ค์„ ์ถ”์ฒœํ•ด! ๐Ÿ“š

๐Ÿ“– ์ถ”์ฒœ ํ•™์Šต ์ž๋ฃŒ


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