page-cover

title

[!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

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

1. Pattern::compile

Pattern의 μƒμ„±μžλŠ” private으둜 μ™ΈλΆ€ 접근이 μ œν•œλ˜μ–΄ μžˆλŠ” λŒ€μ‹ , 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œ compile을 μ œκ³΅ν•œλ‹€. μ»΄νŒŒμΌμ΄λΌλŠ” λ™μž‘μ„ ν‘œν˜„ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ˜μ—ˆμ„ 뿐, μ‹€μ§ˆμ μœΌλ‘œ λ‚΄λΆ€μ˜ μƒμ„±μžλ₯Ό ν˜ΈμΆœν•˜λŠ” ==λŒ€λ¦¬μž==에 λΆˆκ³Όν•˜λ‹€.

/*
 * 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.png

IntelliJμ—μ„œ μ‘°νšŒν•œ API λ¬Έμ„œλ₯Ό μ‚΄νŽ΄λ³΄λ©΄ org.intellij.lang.annotations.MagicConstant λΌλŠ” IDE λ‚΄λΆ€ μ–΄λ…Έν…Œμ΄μ…˜μ΄ μ‘΄μž¬ν•œλ‹€. 이 덕뢄에 compile λ©”μ„œλ“œμ˜ ν”Œλž˜κ·Έλ₯Ό μ„€μ •ν•  λ•Œ, λ‹€μŒκ³Ό 같이 ν•΄λ‹Ή ν΄λž˜μŠ€μ— μ •μ˜λœ μƒμˆ˜λ“€μ„ λ¨Όμ € μΆ”μ²œλ°›μ„ 수 있게 λœλ‹€. (DOTALLλΆ€ν„° UNIX_LINES λͺ¨λ‘ μƒμˆ˜μ— ν•΄λ‹Ή)

compile-ide-hint-flag.png

μ•žμ„  μ •μ˜μ— λͺ…μ‹œλ˜μ–΄ μžˆμ§€λŠ” μ•Šμ§€λ§Œ, regex νŒŒλΌλ―Έν„°μ—λŠ” org.intellij.lang.annotations.RegExp μ–΄λ…Έν…Œμ΄μ…˜μ΄ μ μš©λ˜μ–΄ μžˆλ‹€. μ»΄νŒŒμΌλŸ¬μ—κ² λͺ¨λ‘ λ™λ“±ν•œ Stringμ΄κ² μ§€λ§Œ, 이 μ–΄λ…Έν…Œμ΄μ…˜ 덕뢄에 IDEμ—μ„œλŠ” μ •κ·œ ν‘œν˜„μ‹μœΌλ‘œμ„œμ˜ λ¬Έμžμ—΄λ‘œ μΈμ‹λ˜μ–΄ μ»¨ν…μŠ€νŠΈ μ•‘μ…˜μœΌλ‘œ μ •κ·œμ‹ 검사λ₯Ό μΆ”μ²œλ°›μ„ 수 μžˆλ‹€.

compile-ide-hint-regex.png

2. 클래슀의 μ—­ν• 

==1. νŒ¨ν„΄ λ¬Έμžμ—΄, 2. ν”Œλž˜κ·Έ μ„€μ •== 값을 μ»΄νŒŒμΌν•΄μ„œ μ μ ˆν•œ Matcherλ₯Ό λ§Œλ“œλŠ” 것이 μ£Όμš” λͺ©μ μ΄λ‹€. μ΄λŸ¬ν•œ 클래슀 λ””μžμΈμ€ μ£Όμ„μ—μ„œλ„ 잘 λ“œλŸ¬λ‚œλ‹€.

[!quote] Docs

/*
* 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의 νŒ©ν† λ¦¬ 클래슀==둜 이해할 수 μžˆλ‹€.

/*
 * 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.png ^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 λ¬Έμžλ§Œμ€ μ˜ˆμ™ΈλΌκ³  ν–ˆλ‹€. λ”°λΌμ„œ, 이 μƒνƒœλ‘œ μ‹€ν–‰ν•˜λ©΄ 일반적인 문자 ν•˜λ‚˜λ₯Ό ν¬ν•¨ν•œ 첫 번째 μΌ€μ΄μŠ€λ§Œ μ„±κ³΅ν•œλ‹€.

@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.png

CASE 2: DOTALL

ν”Œλž˜κ·Έλ₯Ό μ„€μ •ν•΄μ£Όλ©΄ λ™μΌν•œ κ·œμΉ™λ„ λ‹€λ₯΄κ²Œ λ™μž‘ν•œλ‹€. DOTALL ν”Œλž˜κ·ΈλŠ” .(DOT)이 μ˜ˆμ™Έλ‘œ λ‘μ—ˆλ˜ line terminator λ¬Έμžλ“€λ„ ν•˜λ‚˜μ˜ 문자둜 μ·¨κΈ‰ν•˜λ„λ‘ λ§Œλ“ λ‹€. λ‹€λ§Œ, "." κ·œμΉ™μ€ μ •ν™•νžˆ ν•˜λ‚˜μ˜ 문자λ₯Ό λŒ€ν‘œν•˜κΈ°μ— 2개의 문자둜 κ΅¬μ„±λœ "\r\n" μΌ€μ΄μŠ€λŠ” λ§€μΉ­λ˜μ§€ μ•ŠλŠ” 것을 λ³Ό 수 μžˆλ‹€.

@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.png

CASE 3: ".+" & DOTALL

+ κΈ°ν˜ΈλŠ” νŠΉμ •ν•œ νŒ¨ν„΄μ„ 1번 이상 λ°˜λ³΅λ˜λŠ” 경우둜 ν™•μž₯μ‹œν‚¨λ‹€. 이λ₯Ό 톡해, 2개의 문자둜 이루어진 "\r\n" μΌ€μ΄μŠ€λ„ ν…ŒμŠ€νŠΈλ₯Ό ν†΅κ³Όν•˜κ²Œ λœλ‹€. 참고둜 ".{1,2}"와 같이 반볡 횟수의 ν•˜ν•œκ³Ό μƒν•œμ„ λͺ…μ‹œν•˜λŠ” 것도 κ°€λŠ₯ν•˜λ‹€.

@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.png

CASE 4: ".+"

DOTALL이 λΉ μ§€λ©΄ λ‹Ήμ—°ν•˜κ²Œλ„ κ²°κ³ΌλŠ” CASE 1κ³Ό 동일해진닀. (κ²°κ³Ό μƒλž΅)