[!quote] Definition

A compiled representation of a regular expression.

A regular expression, specified as a string, must first be compiled into an instance of this class. The resulting pattern can then be used to create a Matcher object that can match arbitrary java.lang.CharSequence character sequences against the regular expression. ==All of the state involved in performing a match resides in the matcher==, so many matchers can share the same pattern.

A typical invocation sequence is thus

java
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();

1. Pattern::compile

Pattern์˜ ์ƒ์„ฑ์ž๋Š” private์œผ๋กœ ์™ธ๋ถ€ ์ ‘๊ทผ์ด ์ œํ•œ๋˜์–ด ์žˆ๋Š” ๋Œ€์‹ , ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ compile์„ ์ œ๊ณตํ•œ๋‹ค. ์ปดํŒŒ์ผ์ด๋ผ๋Š” ๋™์ž‘์„ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜์—ˆ์„ ๋ฟ, ์‹ค์งˆ์ ์œผ๋กœ ๋‚ด๋ถ€์˜ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ==๋Œ€๋ฆฌ์ž==์— ๋ถˆ๊ณผํ•˜๋‹ค.

java
/*
 * This private constructor is used to create all Patterns. The pattern
 * string and match flags are all that is needed to completely describe
 * a Pattern. An empty pattern string results in an object tree with
 * only a Start node and a LastNode node.
 */
private Pattern(String p, int f) { ... }

// ...

public static Pattern compile(String regex) {
	return new Pattern(regex, 0);
}

public static Pattern compile(String regex, int flags) {
	return new Pattern(regex, flags);
}

[!info]+ Info: IDE ํžŒํŠธ

compile-definition

IntelliJ์—์„œ ์กฐํšŒํ•œ API ๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด org.intellij.lang.annotations.MagicConstant ๋ผ๋Š” IDE ๋‚ด๋ถ€ ์–ด๋…ธํ…Œ์ด์…˜์ด ์กด์žฌํ•œ๋‹ค. ์ด ๋•๋ถ„์— compile ๋ฉ”์„œ๋“œ์˜ ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•  ๋•Œ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•ด๋‹น ํด๋ž˜์Šค์— ์ •์˜๋œ ์ƒ์ˆ˜๋“ค์„ ๋จผ์ € ์ถ”์ฒœ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. (DOTALL๋ถ€ํ„ฐ UNIX_LINES ๋ชจ๋‘ ์ƒ์ˆ˜์— ํ•ด๋‹น)

compile-ide-hint-flag

์•ž์„  ์ •์˜์— ๋ช…์‹œ๋˜์–ด ์žˆ์ง€๋Š” ์•Š์ง€๋งŒ, regex ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” org.intellij.lang.annotations.RegExp ์–ด๋…ธํ…Œ์ด์…˜์ด ์ ์šฉ๋˜์–ด ์žˆ๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ์—๊ฒ ๋ชจ๋‘ ๋™๋“ฑํ•œ String์ด๊ฒ ์ง€๋งŒ, ์ด ์–ด๋…ธํ…Œ์ด์…˜ ๋•๋ถ„์— IDE์—์„œ๋Š” ์ •๊ทœ ํ‘œํ˜„์‹์œผ๋กœ์„œ์˜ ๋ฌธ์ž์—ด๋กœ ์ธ์‹๋˜์–ด ์ปจํ…์ŠคํŠธ ์•ก์…˜์œผ๋กœ ์ •๊ทœ์‹ ๊ฒ€์‚ฌ๋ฅผ ์ถ”์ฒœ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

compile-ide-hint-regex

2. ํด๋ž˜์Šค์˜ ์—ญํ• 

==1. ํŒจํ„ด ๋ฌธ์ž์—ด, 2. ํ”Œ๋ž˜๊ทธ ์„ค์ •== ๊ฐ’์„ ์ปดํŒŒ์ผํ•ด์„œ ์ ์ ˆํ•œ Matcher๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ฃผ์š” ๋ชฉ์ ์ด๋‹ค. ์ด๋Ÿฌํ•œ ํด๋ž˜์Šค ๋””์ž์ธ์€ ์ฃผ์„์—์„œ๋„ ์ž˜ ๋“œ๋Ÿฌ๋‚œ๋‹ค.

  • ๋ฌธ์ž์—ด๋กœ ๊ตฌ์ฒดํ™”๋œ ์ •๊ทœ ํ‘œํ˜„์‹์€ ๋จผ์ € ์ด ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋กœ ์ปดํŒŒ์ผ๋˜์–ด์•ผ ํ•œ๋‹ค.
  • 2๊ฐœ์˜ ์œ ์ผํ•œ ์ง๋ ฌํ™” ์š”์†Œ: the pattern string and the flags

[!quote] Docs

java
/*
* A regular expression, specified as a string, must first be compiled into an instance of this class.
*
* Pattern has only two serialized components: The pattern string
* and the flags, which are all that is needed to recompile the pattern
* when it is deserialized
*/

2-A. Matcher ํŒฉํ† ๋ฆฌ

์‹ค์งˆ์ ์œผ๋กœ ๋ฌธ์ž์—ด์„ ๋ฐ›์•„ ํŒจํ„ด ๋น„๊ต๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์€ Matcher์˜ ์—ญํ• ์ด๋‹ค. Matcher ํด๋ž˜์Šค๋Š” public์œผ๋กœ ์—ด๋ ค์žˆ์ง€๋งŒ, ์ƒ์„ฑ์ž๋Š” package-private๋กœ ๊ตฌ์„ฑ๋œ ๊ฒƒ์„ ๋ณด๋ฉด, Pattern์€ ์ •๊ทœ ํ‘œํ˜„์‹ ์ปดํŒŒ์ผ๋กœ ์ดˆ๊ธฐํ™”๊ฐ€ ํ•„์š”ํ•œ ==Matcher์˜ ํŒฉํ† ๋ฆฌ ํด๋ž˜์Šค==๋กœ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.

java
/*
 * An engine that performs match operation on a java.lang.CharSequence character sequence by interpreting a Pattern.
 *
 * A matcher is created from a pattern by invoking the pattern's Patter#matcher matcher method. (...)
 */
public final class Matcher implements MatchResult {

    Matcher() {} // No default constructor.

    /**
     * All matchers have the state used by Pattern during a match.
     */
    Matcher(Pattern parent, CharSequence text) { ... )

3. Flags

ํ”Œ๋ž˜๊ทธ๋Š” ๋™์ผํ•œ ๋ฌธ๋ฒ•์ด ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„๋“ค์„ ์ปค๋ฒ„ํ•ด์ค€๋‹ค.

3-A. Pattern.DOTALL

์ƒ์ˆ˜์— ๋Œ€ํ•œ ์„ค๋ช…์„ ๋ณด๋ฉด line terminator๋ผ๋Š” ์šฉ์–ด๊ฐ€ ๋‚˜์˜ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ==์ค„ ๋ฐ”๊ฟˆ==์„ ์˜๋ฏธํ•˜๋Š” ๋ฌธ์ž๋“ค์„ ์ด์นญํ•˜๋Š” ๊ฐœ๋…์ธ๋ฐ, LF, CRLF ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ค„ ๋ฐ”๊ฟˆ์„ ํ‘œํ˜„ํ•˜๋Š” ๋ฌธ์ž๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ๋งŒ ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

์ •๊ทœ ํ‘œํ˜„์‹์—์„œ . (DOT)์€ ์ž„์˜์˜ ํ•˜๋‚˜์˜ ๋ฌธ์ž์— ๋Œ€ํ•œ ํ‘œ์ƒ์ด์ง€๋งŒ, ๊ธฐ๋ณธ์ ์œผ๋กœ line terminator๋Š” ์˜ˆ์™ธ๋กœ ๋‘”๋‹ค. DOTALL์€ ์ด๋Ÿฌํ•œ ๊ธฐ๋ณธ ๋™์ž‘์„ ์ˆ˜์ •ํ•˜์—ฌ, ๊ทธ์•ผ๋ง๋กœ _์ (DOT)์ด ๋ชจ๋“ (ALL) ๋ฌธ์ž๋ฅผ ๋Œ€ํ‘œ_ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•ด์ฃผ๊ธฐ ์œ„ํ•œ ์ปดํŒŒ์ผ ์˜ต์…˜์ด๋‹ค. flags-dotall ^4b506c

3-A-1. LineTerminator ์˜ˆ์‹œ

[!quote] Definition A one- or two-character sequence that ==marks the end of a line== of the input character sequence.

  • '\n'
  • '\r'
  • "\r\n"
  • '\u0085'
  • '\u2028'
  • '\u2029'

java.util.regex.Pattern#lt

Java์˜ char ์ž๋ฃŒํ˜•์€ 2 Byte์ด๊ธฐ์— ๋‹จ์ผ 16 bit ์œ ๋‹ˆ์ฝ”๋“œ๊นŒ์ง€๋Š” ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ, "\r\n"๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  ๋ฌธ์ž๊ฐ€ ์ž‘์€ ๋”ฐ์˜ดํ‘œ '๋กœ ๋ฌถ์—ฌ ์žˆ๋Š” ๊ฒƒ์„ ์ฐธ๊ณ ํ•˜์ž.

Pattern์„ ์ดˆ๊ธฐํ™” ํ•  ๋•Œ, DOTALL ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์‹ค์ œ ์ •๊ทœ์‹ ๋งค์นญ์— ์–ด๋–ค ๊ฒฐ๊ณผ๋กœ ๋‚˜ํƒ€๋‚˜๋Š”์ง€ ํ•™์Šต ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด์ž.

CASE 1: ๊ธฐ๋ณธ DOT

๋ถ„๋ช… .(DOT)์€ ์ž„์˜์˜ ๋ฌธ์ž ํ•˜๋‚˜๋ฅผ ๋Œ€ํ‘œํ•˜์ง€๋งŒ, line terminator ๋ฌธ์ž๋งŒ์€ ์˜ˆ์™ธ๋ผ๊ณ  ํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์ด ์ƒํƒœ๋กœ ์‹คํ–‰ํ•˜๋ฉด ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ž ํ•˜๋‚˜๋ฅผ ํฌํ•จํ•œ ์ฒซ ๋ฒˆ์งธ ์ผ€์ด์Šค๋งŒ ์„ฑ๊ณตํ•œ๋‹ค.

java
@ParameterizedTest
@ValueSource(strings = {" ", "\n", "\r", "\r\n", "\u0085", "\u2028", "\u2029"})
void matchLineTerminators(String lineTerminator) {
	Pattern p = Pattern.compile(".");
	Matcher m = p.matcher(lineTerminator);
	assertTrue(m.matches());
	assertEquals(lineTerminator, m.group());
}

flags-dotall-lt-result1

CASE 2: DOTALL

ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋™์ผํ•œ ๊ทœ์น™๋„ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•œ๋‹ค. DOTALL ํ”Œ๋ž˜๊ทธ๋Š” .(DOT)์ด ์˜ˆ์™ธ๋กœ ๋‘์—ˆ๋˜ line terminator ๋ฌธ์ž๋“ค๋„ ํ•˜๋‚˜์˜ ๋ฌธ์ž๋กœ ์ทจ๊ธ‰ํ•˜๋„๋ก ๋งŒ๋“ ๋‹ค. ๋‹ค๋งŒ, "." ๊ทœ์น™์€ ์ •ํ™•ํžˆ ํ•˜๋‚˜์˜ ๋ฌธ์ž๋ฅผ ๋Œ€ํ‘œํ•˜๊ธฐ์— 2๊ฐœ์˜ ๋ฌธ์ž๋กœ ๊ตฌ์„ฑ๋œ "\r\n" ์ผ€์ด์Šค๋Š” ๋งค์นญ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

java
@ParameterizedTest
@ValueSource(strings = {" ", "\n", "\r", "\r\n", "\u0085", "\u2028", "\u2029"})
void matchLineTerminators(String lineTerminator) {
	Pattern p = Pattern.compile(".", Pattern.DOTALL);
	Matcher m = p.matcher(lineTerminator);
	assertTrue(m.matches());
	assertEquals(lineTerminator, m.group());
}

flags-dotall-lt-result2

CASE 3: ".+" & DOTALL

+ ๊ธฐํ˜ธ๋Š” ํŠน์ •ํ•œ ํŒจํ„ด์„ 1๋ฒˆ ์ด์ƒ ๋ฐ˜๋ณต๋˜๋Š” ๊ฒฝ์šฐ๋กœ ํ™•์žฅ์‹œํ‚จ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด, 2๊ฐœ์˜ ๋ฌธ์ž๋กœ ์ด๋ฃจ์–ด์ง„ "\r\n" ์ผ€์ด์Šค๋„ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜๊ฒŒ ๋œ๋‹ค. ์ฐธ๊ณ ๋กœ ".{1,2}"์™€ ๊ฐ™์ด ๋ฐ˜๋ณต ํšŸ์ˆ˜์˜ ํ•˜ํ•œ๊ณผ ์ƒํ•œ์„ ๋ช…์‹œํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

java
@ParameterizedTest
@ValueSource(strings = {" ", "\n", "\r", "\r\n", "\u0085", "\u2028", "\u2029"})
void matchLineTerminators(String lineTerminator) {
	Pattern p = Pattern.compile(".+", Pattern.DOTALL);
	Matcher m = p.matcher(lineTerminator);
	assertTrue(m.matches());
	assertEquals(lineTerminator, m.group());
}

flags-dotall-lt-result3

CASE 4: ".+"

DOTALL์ด ๋น ์ง€๋ฉด ๋‹น์—ฐํ•˜๊ฒŒ๋„ ๊ฒฐ๊ณผ๋Š” CASE 1๊ณผ ๋™์ผํ•ด์ง„๋‹ค. (๊ฒฐ๊ณผ ์ƒ๋žต)