Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- boolean
- htmlFor
- VAR
- createtextnode
- FOR
- Append
- 학습법 #집중력
- const
- Openlayers
- createElement
- appendChild
- input
- Let
Archives
- Today
- Total
Atomic Habits
[JAVA] 정규식/정규표현식 본문
https://offbyone.tistory.com/400 : 전반적인 정리 참조
https://hermeslog.tistory.com/310 : 특수문자 처리 / 플래그(Pattern.MULTILINE (?m)) 참조
http://egloos.zum.com/sweeper/v/3064808 : 예문 참조
https://ohgyun.com/392 : 단어 경계(\b) 참조
1. 정규표현식에 쓰이는 특수문자
1. '.'
임의의 한 문자
ex)
s.e -> sae, sbe, sce, sde, ...
.ce -> ace, kce, dce, ...
*** a[.]b는 a와 b 사이에 ~~임의의 한 문자~~가 아닌 실제 Dot . 을 찾는다.**
2. '*'
바로 앞의 문자가 없거나 하나 이상
ex)
s*e -> e, se, see, ssse, ...
abc* -> ab, abc, abcc, abccc, ...
h*im -> im, him, hhim, hhhim, ...
3. '+'
바로 앞의 문자가 하나 이상
ex)
s+e -> se, sse, ssse, ...
4. '?'
바로 앞의 문자가 없거나 하나
ex)
th?e -> e, the 이 두가지표현이 유일하겠지.
5. '^'
바로 뒤의 문자열로 시작.
ex)
^The -> The girl is, Theather, ... (뒷부분부터 공백까지 검사)
^a?bc -> bc, abc, ...
^.e -> he, me, request, settle, ...
^s.e? -> sa, sae, sb, sbe, ... (e는 나와도 되고 안나와도 되고)
6. '$'
바로 앞의 문자열로 종료
ex)
a?bc$ -> eeabe, seebc, bc, ...
+.e$ -> onthetoe, bctae, appetittle, ...
s?c+$ -> e, se, ee, eee, seee, seee, ...
7. '[]'
[] 안에 있는 문자 중 하나, 범위는 '-'로 지정
ex)
[ab]cd -> acd, bcd, ...
[a-z] -> 영문 소문자 (a부터 z까지)
[a-zA-Z] -> 영문자(대소문자)
[0-9] -> 0부터 9까지의 숫자
ag[a-z] -> aga, agbcd, agzzz, ...
^ab[cd]ef -> abcef, abdef, ...
^[a-zA-Z] -> 영문자로 시작
^[a-zA-Z_] -> 아이디 검사할 때 첫글자가 영문자와 '_' 만 쓰도록 할때
^[가-힣] -> 한글로 시작해야 할 때
[^a-zA-Z0-9] -> ^이 안으로 들어가면 제외(부정)의 의미가 된다. 영문자나 숫자로 시작할 수 없을 때
[a-zA-Z0-9]$ -> 영문자나 숫자로 종료
[가-힣] -> 한글(완성형)만 가능. ㅋㅋㅋ 같은 문자는 제외
[abc] -> 이 안에 있는 문자중에 하나. 즉, a b c 중에 하나의 문자.
8. '{}'
{} 앞의 문자나 문자열 출현 횟수
ex)
a{2}b -> aab, ... a가 꼭 2번 나와야 한다는 뜻.
a{2,}b -> aab, aaab, aaaab, ... a가 최소 2번 이상 나오도록 하라는 뜻.
a{2, 3}b -> aab, aaab, ... a는 최소 2번 최대 3번 나오도록 하라는 뜻.
9. '()'
()안에 있는 문자를 그룹화
ex)
a(bc){2} -> abcbc, ... a다음 bc가 2번 나와야 한다는 뜻..
a(bc)* -> abcbcbc, ... a다음 bc의 출현횟수는 무한대가 가능하다는 뜻.
10. '|'
or 연산자
ex)
he|she -> he, she is, ...
(he|she)is -> heis, sheis, ...
11. 특수 문자 사용
^ [] $ () | * + ? {} \\
앞에 \\ 붙여서 사용해야함
ex)
\\*+ : * 가 하나 이상 포함된 패턴
\\d : 순수한 숫자, 정수값, 0-9
\\d{2,3}-/d{3,4}-/d{4} : 전화번호 정규식. -? 하이퍼뒤에 물음표가 있으면 하이퍼가 있어도 되고 없어도 된다는 뜻.
\\D : \\d와 반대 (숫자를 제외한 나머지)
\\w : [a-zA-Z0-9] 의 줄임표현.
\\W : [^a-zA-Z0-9] 영문자와 숫자만 아니면 된다는 뜻.
\\s : 공백 문자
\\S : \\s와 반대 (공백 문자를 제외한 나머지)
2. 정규식 사용법
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegExr {
public RegExr() {
// TODO Auto-generated constructor stub
}
public static void main(String[] args) {
// 기본 정규식 사용
String target = "자유석 시간권(10시간);자유석 기간권(4주);5인실 일회권(3시간)"; //
String regEx = ".{3}\\\\s.{5,9}\\\\)+";
Pattern pat = Pattern.compile(regEx);
Matcher mat = pat.matcher(target);
while (mat.find()) {
System.out.println(mat.group()); // 매치된 문자열 반환
}
// 특정 문자열을 대체하는 정규식
String removeRegEx = ".{3}\\\\s기.{5,9}\\\\)+;?"; // 비기간권만 남기기 replaceAll
String removeRegEx = ".{3}\\\\s(?!기).{5,9}\\\\);?"; // 기간권만 남기기 replaceAll
System.out.println(target.replaceAll(removeRegEx , ""));
System.out.println(target.replaceAll(removeRegEx , "").split(";").length);;
String[] ary = target.replaceAll(removeRegEx , "").split(";");
System.out.println(ary[0]);
System.out.println(ary[1]);
}
}
* 반환하는 타입
group() : 매치된 문자열 자체
start() : 매치된 문자열 시작 위치
end() : 매치된 문자열 종료 위치
span() : 매치된 문자열 (시작, 종료) 위치 튜플
3. 정규식 함수 ( 일치 여부, 추출, 치환 )
자바 정규식 기본정리 : Matcher, Pattern, find(), group()
정규식을 사용하면 문자열(String)이 특정 패턴과 일치하는지 여부를 확인하거나, 패턴에 맞는 값을 찾아내거나, 해당 값을 새로운 값으로 바꿀 수 있다.
이 방법이 일목요연하게 작성되어 있는 곳이 마땅히 보이지 않았기에 직접 정리해서 써본다.
1. matches (일치하는지 확인)
target 은 대상이 되는 문자열(문장)을 담는 변수이고, regEx는 정규식(Regular Expression) 을 담는 변수라고 해보자.
public void isEqualRegEx() {
String target = "나는 2008년도에 입학했다.";
String regEx = ".*\\\\d{1}.*";
// String regEx = ".*[0-9].*"; 와 동일함
if (target.matches(regEx)) {
System.out.println("일치");
} else {
System.out.println("불일치");
}
}
여기서 regEx 는 ".*\\\\d{1}.*"; 이다. 즉 여기서 target.matches(regEx) 는 숫자가 1개라도 포함되어 있느냐고 묻는 것이다.
(".*" 는 모든 복수의 문자이고, "\\\\d{1}"는 한 자리 숫자이므로.)
이 경우 2008, 이렇게 숫자가 4개나 있으니까 당연히 "일치"가 출력되겠지.
만약 regEx 값이 바뀐다면 어떨까?
regEx == ".*\\\\d{1}.*" -> 일치
regEx == ".*[0-9].*" -> 일치 ([0-9]는 \\\\d{1} 와 정확히 같은 뜻임)
regEx == ".*[0-9][0-9][0-9][0-9].*" -> 일치 (숫자 4자리)
regEx == ".*[0-9][0-9][0-9][0-9][0-9].*" -> 불일치 (숫자 5자리)
regEx == ".*\\\\d{1,4}.*" -> 일치 (숫자 1자리 이상 4자리 이하)
regEx == ".*\\\\d{2,5}.*" -> 일치 (숫자 2자리 이상 5자리 이하)
regEx == ".*\\\\d{5,6}.*" -> 불일치 (숫자 5자리 이상 6자리 이하)
요런 식으로 되겠다.
2. replaceAll (패턴에 맞는 값을 새로운 값으로 치환)
public void replaceRegEx() {
String target = "나는 2008년도에 입학했다.";
String regEx = "[0-9]";
Pattern pat = Pattern.compile(regEx);
Matcher m = pat.matcher(target);
String result = m.replaceAll("2"); // 패턴과 일치할 경우 "2"로 변경
System.out.println("출력 : " + result);
// 출력 : 나는 2222년도에 입학했다.
}
여기서 regEx 변수 값은 "[0-9]" 이다. 그러니까 위 소스는 1자리 숫자들을 "2"로 치환하게 된다.
결과는 당연히 "나는 2222년도에 입학했다."가 된다.
위 함수를 3줄로 줄이면 아래와 같다.
public void replaceRegEx() {
String target = "나는 2008년도에 입학했다.";
String regEx = "[0-9]";
System.out.println("출력 : " + target.replaceAll(regEx, "2"));
}
String 객체에 replaceAll 라는 멤버함수가 있다. 이게 더 낫다.
전자는 Pattern 객체와 Matcher 객체를 추가로 임포트하는데, 후자는 String 객체만 있으면 되니까.
3-1. find(), group() (패턴에 맞는 값 1개씩 찾아내기)
find랑 group이라는 함수가 특이한데 Matcher 의 멤버함수다.
public void findRegEx(){
String target = "나는 2008년도에 입학했다.";
String regEx = "[0-9]";
// 정규식(regEx)을 패턴으로 만들고,
Pattern pat = Pattern.compile(regEx);
// 패턴을 타겟 스트링(target)과 매치시킨다.
Matcher match = pat.matcher(target);
System.out.println(match.find()); // true
System.out.println(match.group()); // 2
System.out.println(match.find()); // true
System.out.println(match.group()); // 0
System.out.println(match.find()); // true
System.out.println(match.group()); // 0
System.out.println(match.find()); // true
System.out.println(match.group()); // 8
System.out.println(match.find()); // false
System.out.println(match.group()); // 에러 발생! (IllegalStateException)
}
Pattern에 compile로 정규식(regEx)을 담고, Matcher에 타겟 스트링(target)을 담는게 먼저다.
그 다음 Matcher의 find() 함수를 쓰면 1번째 값을 찾아내고, true 혹은 false를 반환한다.
group() 을 쓰면 방금 찾은 1번째 스트링이 튀어나온다.
다시 find()를 쓰면 2번째 값을 찾고, group()을 쓰면 2번째 값이 튀어나오고... 이런 식이다.
보면 2, 0, 0, 8 까지 잘 가다가 (숫자가 더 이상 없으므로 당연히) 5번째에서 에러가 나는데,
따라서 에러가 나지 않도록 코드를 쓴다면 아래와 같이 작성해야 마땅하겠다.
...(전략)...
if (match.find()) {
System.out.println(match.group());
}
...(후략)...
3-2. find(), group() (패턴에 맞는 값 모두 찾아내기)
3-1에서 설명한 바를 잘 정리한게 아래 메서드다.
public void findAllRegEx(){
String target = "나는 2008년도에 입학했다.";
String regEx = "[0-9]";
Pattern pat = Pattern.compile(regEx);
Matcher match = pat.matcher(target);
int matchCount = 0;
while (match.find()) {
System.out.println(matchCount + " : " + match.group());
matchCount++;
}
System.out.println("총 개수 : " + matchCount);
// 0 : 2
// 1 : 0
// 2 : 0
// 3 : 8
// 총 개수 : 4
}
4. 긍정형/부정형 전방/후방 탐색
* 긍정형은 찾아 달라는 의미이고, 부정형은 찾지 말아달라는 의미이다.
* 전방 : (괄호)를 기준으로 궁극적으로 출력하고자 하는 문자열이 앞에 있다. (?=) , (?!)
* 후방 : (괄호)를 기준으로 궁극적으로 출력하고자 하는 문자열이 뒤에 있다. (?<=) , (?<!)
* 긍정형 전방탐색 - 앞으로 찾기
일치하는 텍스트는 텍스트 자체는 소비하지(consume) 않고, 그 앞에 무엇이 오는지 출력한다.
* 긍정형 후방탐색 - 뒤로 찾기
일치하는 텍스트는 텍스트 자체는 소비하지(consume) 않고, 그 뒤에 무엇이 오는지 출력한다.
* 부정형 전방탐색 - 앞으로 찾기
불일치하는 텍스트 문자열 찾아서(소비하지 않고), 그 앞에 무엇이 오는지 출력한다.
* 긍정형 후방탐색 - 뒤로 찾기
불일치하는 텍스트 문자열 찾아서(소비하지 않고), 그 뒤에 무엇이 오는지 출력한다.
부정형 전방 탐색 (?!) vs (?=) 긍정형 전방 탐색
부정형 후방 탐색 (?<!) vs (?<=) 긍정형 후방 탐색
------------------------------------------------
예시
문자열 : /Add_it/applylist?field=&query=¤tPage=2 ->
(?<=\\&).+ ---> query=¤tPage=2
(?<=field=).*(?<=\\&) ---> &query=&
(?<=field=).*(?=\\&query) ---> ""
문자열 : /Add_it/applylist?field=aa&query=¤tPage=2 ->
(?<=field=).*(?=\\&query) ---> aa
문자열 : /Add_it/applylist?field=aa&query=bb¤tPage=2
(?<=query=).*(?=\\¤tPage) ---> bb
(?<=currentPage=)\\d* ---> 2
------------------------------------------------
* 긍정형 전방/후방 탐색
일치 조건에 포함은 시키되, 최종 결과에는 제외시켜달라는 의미이다.(=소비하지 않는다)
정규표현식 : (긍정형 전방탐색)(기본 정규표현식)
(긍정형 전방탐색) 조건과 (기본 정규표현식) 조건 동시에 만족하는 텍스트를 찾되
(기본 정규표현식) 만 출력한다.
전방 (?<= 표현식)
후방 (?= 표현식 )
(?<= \\<\\w+\\>) .* (?= \\<\\/\\w+\\>)
(?<= \\<([Tt][Ii][Tt][Ll][Ee])\\>) .*? (?= \\<\\/\\1\\>)
<TITLE>SooKkaRak's homepage</TITLE>
* 부정형 전방/후방 탐색
부정형 탐색은 비교적 덜 쓰는 방법이다.
부정형 전방 탐색
정규표현식 : (부정형 전방탐색)(기본 정규표현식)
(부정형 전방탐색) 조건과는 일치하지 않고 (기본 정규표현식) 조건만 일치하는 텍스트를 찾고,
부정형 후방 탐색도 이와 비슷하게, 뒤쪽에서 지정한 패턴과 일치하지 않는 텍스트를 찾는다.
*
부정형 전후방 탐색은 다음과 같이 사용한다.
부정형 전방 탐색 (?!) vs (?=) 긍정형 전방 탐색
부정형 후방 탐색 (?<!) vs (?<=) 긍정형 후방 탐색
즉, 긍정형 전후방 탐색의 = 대신 !를 사용하면 된다.
* 예문
서울 성북군 서성동 1번지
동읍리면 기준 앞 글자 선택 : .*[동,읍,리,면](?<!\\s)
동읍리면 기준 뒷 글자 선택 : (?<=[동]\\s).*
* 예문
다음은 긍정형 후방 탐색과 부정형 후방 탐색 사이에 어던 차이가 있는지 알아보는 예제이다.
예제 본문에 가격과 수량을 나타내는 숫자들이 있다.
우선 간단하게 가격만 얻어보자.
[예문]
I paid $30 for 100 apples, 50 oranges, and 60 pears.
I saved $5 on this order.
[긍정형 후방탐색] (?<=\\$) \\d+
[결과]
30
5
이번에는 가격이 아니라, 수량을 찾아보자.
[부정형 후방탐색] (?<!\\$) \\d+
[결과]
0 100 500 60
$30의 0 역시 $ 다음 위치가 아니기에 덩달아 같이 결과에 포함되어 버렸다.
이럴 땐 깔끔하게 단어 경계를 붙여 문제를 해결하면 되겠다.
\\b : 앞 혹은 뒤에 문자, 숫자, 특수문자 등이 오지 않고 다른 단어와 경계가 있어야 한다는 조건이다.
[부정형 후방탐색 + 단어 경계조건] \\b (?<!\\$) \\d+ \\b
[결과]
100 500 60
'IT > Regular Expressions' 카테고리의 다른 글
정규식 간단 강좌 1-8편 (0) | 2021.11.24 |
---|---|
정규 표현식 (좀 더) 깊이 알아보기 (0) | 2021.11.24 |
풀어 쓴 정규식 (0) | 2021.11.24 |
정규식 전후방탐색(vim 포함) (0) | 2021.11.20 |
[Oracle, postgreSQL] 정규식 REGEXP_REPLACE 함수 (0) | 2021.11.20 |
Comments