1. IP 주소 패턴
IP 주소는 0-255까지 숫자 범위가 있기 때문에 각 자리수별로 올 수 있는 숫자를 생각하여 정규표현식으로 표현해야 한다.
아이피 숫자가 한자리 혹은 두자리 수 일 경우에는 아무런 숫자가 와도 된다.
세자리 숫자일 경우에는 생각할 꺼리가 많아지는데, 맨 앞자리가 0,1,2 인 경우에 따라 각각의 케이스들을 | (pipeline) 으로 연결해 주면 된다.
아이피 숫자는 맨 앞자리가 0이어도 유효하기 때문에 (0|1)일 경우에는 뒤에 두자리가 [0-9] 아무 숫자가 와도 된다. 맨 앞자리가 2일 경우에는 두 가지 분기로 나누어야만 하는데,
두번째 자리가 [0-4]이면은 일의 자리는 [0-9] 아무숫자나 와도 되지만 앞자리 두개가 25일 경우 일의 자리는 0-5까지만 와야 한다.
아래 예시에 IP 매칭에 대한 정규표현식을 작성하였는데 두 개는 동일한 내용이다.
두 번째가 [0-9]를 \\d로 표현했기 때문에 더 간결해 보인다.
\\. 이 부분도 점 그 자체로 검색해야 하므로 이스케이프 문자가 붙어있다.
String pattern = "([0-9]{1,2}|(0|1)[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.([0-9]|[0-9][0-9]|(0|1)[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}";
String pattern = "(\\d{1,2}|(0|1)\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d{1,2}|(0|1)\\d{2}|2[0-4]\\d|25[0-5])){3}";
2. 중복된 단어 찾기
아래와 같은 문자열에서 여러번 중복된 단어들을 제거하고 첫번째 문자열만 남긴다. 대소문자의 구분은 없다.
test Matched matched matched matchedtest String string String matched
이걸 풀기 위해서 정규표현식의 grouping 을 이용하면 쉽게 풀린다. group() 관련 메소드는 API문서를 참고하면 된다.
연속으로 중복되는 단어들을 전부 찾아내고, 그 중에 첫번째 단어만 남겨야 한다.
패턴은 아래와 같다.
String pattern = "\\b(\\w+)(?:\\W\\1\\b)+";
하나씩 의미를 파악해보자면,
\\b(\\w+)
\\b로 단어의 경계를 지정하고 한 글자 이상의 단어를 찾는다. 그 단어 한개가 첫번째 그룹이다.
(?:\\W\\1\\b)+
(?:X) 이 문법은 이 괄호 안에 잡히는 대상은 그룹으로 잡지 않겠다는 의미이다. 또한 마지막에 + 로 되어있는데 이런 그룹이 연속으로 1개 이상이라는 의미이다.
\\W는 word가 아닌 문자열을 의미한다. 중복된 단어 사이에 공백이나 , . 등이 있는 경우도 포함하기 위함이다.
\\1 은 첫번째 그룹에서 가장 최근에 매치된 동일한 텍스트를 의미한다.
만약 그룹이 여러개라면 \\n 이런 식으로 표현 가능하며, n번째 그룹에서 가장 최근에 매치된 텍스트를 골라낼 수 있다.
위 예제에서는 만약 단어 Matched가 매치되면 그 다음에 이 단어가 \\1자리에 위치하는 것이다.
마지막에 단어의 경계를 나타내는 \\b를 명시하지 않으면 위 예시에서 matchedtest에서 matched까지 찾아내게 된다.
이제 이 정규표현식을 통해 중복을 제거한다.
핵심은 하나의 문장을 입력받아서 하나의 문장을 Matcher가 검사하는데 하나의 문장 내에서도 while문에 Matcher로 find()를 하면서 나란히 중복되는 문자열을 찾는다.
그리고 그 찾아낸 문자열의 m.group() 을 m.group(1) 로 replaceAll() 을 한다.
m.group()은 위 정규표현식에서 전체에 매칭되는 Full match를 의미한다.
test Matched matched matched matchedtest String string String matched
Matched matched matched 가 첫번째 m.group()이 되고 Matched는 첫번째 그룹의 값인 m.group(1)이 된다.
String string String은 그 다음 while 루프에서 찾아지며 동일하게 첫번째 String으로 replaceAll이 될 것이다.
public class DuplicateWords {
public static void main(String[] args) {
String regex = "\\b(\\w+)(?:\\W\\1\\b)+";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); //대소문자 구분없이 찾는다
Scanner in = new Scanner(System.in);
int numSentences = Integer.parseInt(in.nextLine());
while (numSentences-- > 0) {
String input = in.nextLine();
Matcher m = p.matcher(input);
// Check for subsequences of input that match the compiled pattern
while (m.find()) {
input = input.replaceAll(m.group(), m.group(1));
}
// Prints the modified sentence.
System.out.println(input);
}
in.close();
}
}
3. 아이디 제한 걸기
아이디 생성 조건은 다음과 같다.
- 문자열 길이는 8자 이상~ 30자 이하.
- 첫 시작 문자는 반드시 알파벳이어야 함.
- 알파벳 대소문자, 숫자, _ 가능
- 특수문자 불가
위 조건은 아래와 같이 심플하게 정규표현식으로 가능하다.
String pattern = "\\p{Alpha}[\\w]{7,29}";
7,29로 범위를 지정한 이유는 앞에 \\p{Alpha} 가 한자리를 차지하기 때문에 1씩 빼주었다.
4. Html Tag 안의 텍스트 내용 추출하기
아래는 Tag의 내용을 추출하는 코드이다.
다만 태그의 열고 닫는 문법이 정확해야 그 내용을 추출해야한다. 만약 <tag>내용...<tag>이라면 올바른 태그 형식이 아니다.
public class Solution{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int testCases = Integer.parseInt(in.nextLine());
while(testCases>0){
String line = in.nextLine();
String pattern = "<(.+)>([^<>]+)</\\1>";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(line);
boolean flag = false;
while(m.find()){
flag = true;
System.out.println(m.group(2));
}
if(!flag)
System.out.println("None");
testCases--;
}
}
}
String pattern = "<(.+)>([^<>]+)</\\1>";
먼저 여는 태그 부분의 꺽쇠 안의 내용 그룹 한개와, content에 해당하는 부분을 2개의 그룹으로 만들고, 닫는 태그는 \\1을 이용하여 첫번째 그룹의 캡쳐된 문자열을 넣도록 하여 태그 문법이 맞도록 하였다.
content에는 < 혹은 >를 제외한 모든 문자가 들어가도록 되어있다.
<h1><a>contents</a>invalid</h1> 이런 식의 형태에서 추출 해야 할 때 <a>태그 안에 있는 contents 문구는 유효하지만, invalid 문구는 유효하지 않도록 하는 것이 문제의 규칙이다.
댓글 없음:
댓글 쓰기