공부했던 자료 정리하는 용도입니다.

재배포, 수정하지 마세요.

 

 

 

 

 

정규식(Regular Expression) - java.util.regex패키지

  정규식이란 텍스트 데이터 중에서 원하는 조건(패턴, pattern)과 일치하는 문자열을 찾아내기 위해 사용하는 것으로 미리 정의된 기호와 문자를 이용해서 작성한 문자열을 말한다. 정규식을 이용하면 많은 양의 텍스트 파일 중에서 원하는 데이터를 손쉽게 뽑아낼 수 있고, 입력된 데이터가 형식에 맞는지 체크할 수도 있다. Java API문서에서  java.util.regex.Pattern 에 정규식에 사용되는 기호와 작성방법이 모두 설명되어 있다.

 

 

자주 사용되는 패턴

정규식패턴 설  명 결  과
c[a-z]*  c 로 시작하는 영단어 c, ca, car, combat, count …
c[a-z]  c 로 시작하는 2자리 영단어 ca, ci 
c[a-zA-Z]  c 로 시작하는 2자리 영단어(대소문자 구분 X) cA, co, aP
c[a-zA-Z0-9]
c\w
 c 로 시작하고 숫자 + 영어로 조합된 2글자 c0, cE, cm
.* 모든 문자열  
c.  c 로 시작하는 2자리 문자열 c. ca, c#
c.*  c 로 시작하는 모든 문자열(기호포함) c., c0, c#, count 
c\.  c. 와 일치하는 문자열 . 은 패턴작성에 사용되는 문자라 escape문자인  \ 를 사용해야함 c.
c\d
c[0-9]
 c 와 숫자로 구성된 2자리 문자열 c0
c.*t  c 로 시작하고  t 로끝나는 모든 문자열 combat, count
[b|c].*
[bc].*
[b-c].*
 b 또는  c 로 시작하는 문자열 bonus, cA, bat, c., c0, c#
[^b|c].*
[^bc].*
[^b-c].*
 b 또는  c 로 시작하지 않는 문자열 date, disc
.*a.*  a 를 포함하는 모든 문자열
 *  : 0 또는 그 이상의 문자
ca, combat, date
.*a.*  a 를 포함하는 모든 문자열
 +  : 1 또는 그 이상의 문자,  + 는  * 과 달리 반드시 하나 이상의 문자가 있어야 하므로  a 로 끝나는 단어는 포함되지 X
date, bat
[b|c].{2}  b 또는  c 로 시작하는 세자리 문자열
( b 또는  c 다음에 두자리이므로 모두 세자리)
bat, car
0\\d{1,2}  0 으로 시작하는 최소 2자리 최대 3자리 숫자(0포함)  
\\d{3,4} 최소 3자리 최대 4자리의 숫자 512, 8516…
\\d{4} 4자리의 숫자  

 

 

 

 

문자열 검사

package Example;

import java.util.regex.*;	// Pattern과 Matcher가 속한 패키지

public class Example {
	public static void main(String[] args) {
		String[] data = {"bat", "baby", "bonus", "cA", "ca", "co", "c.", "c0", "car", "combat", "count", "date", "disc"};
		
		Pattern p = Pattern.compile("c[a-z]*");	//c로 시작하는 소문자영단어
		
		for(int i = 0 ; i < data.length ; i++) {
			Matcher m = p.matcher(data[i]);
			if(m.matches())
				System.out.print(data[i] + ",");
		}
	}
}

 

 


 정규식을 정의하고 데이터를 비교하는 과정 

1. 정규식을 매개변수로  Pattern 클래스의  static 메서드인  Pattern compile(String regex) 을 호출하여  Pattern 인스턴스를 얻는다.
Pattern p = Pattern.compile("c[a-z]*");

2. 정규식으로 비교할 대상을 매개변수로  Pattern 클래스의  Matcher matcher(CharSequence input) 를 호출해서  Matcher 인스턴스를 얻는다.
Matcher m = p.matcher(data[i]);

3.  Matcher 인스턴스에  boolean matcher( ) 를 호출해서 정규식에 부합하는지 확인한다.
if(m.matches( ))

문자 배열인  data 에 담긴 문자열을 정규식과 맞는지 검사해서 일치하는 문자열을 출력하는 예제이다.  Pattern 은 정규식을 정의하는 데 사용되고  Matcher 는 정규식(패턴)을 데이터와 비교하는 역할을 한다.

 

 

 

 

Matcher 클래스의 메서드들

메서드 설  명
find( ) 패턴이 일치하는 경우 true를 반환, 불일치하는 경우 false반환(여러개가 매칭되는 경우 반복실행하면 일치하는 부분 다음부터 이어서 매칭됨)
find(int start)  start 위치 이후부터 매칭검색
start( ) 매칭되는 문자열의 시작위치 반환
start(int group) 지정된 그룹이 매칭되는 시작위치 반환
end( ) 매칭되는 문자열 끝위치의 다음 문자위치 반환
end(int group) 지정된 그룹이 매칭되는 끝위치의 다음 문자위치 반환
group( ) 매칭된 부분을 반환
group(int group) 그룹화되어 매칭된 패턴중  group 번째 부분 반환
groupCount( ) 괄호로 지정해서 그룹핑한 패턴의 전체 개수 반환
matches( ) 패턴이 전체 문자열과 일치할 경우 true반환

 

 

package Example;

import java.util.regex.*;	// Pattern과 Matcher가 속한 패키지

public class Example {
	public static void main(String[] args) {
		String source = "HP:011-1111-1111, HOME:02-999-9999";
		String pattern = "(0\\d{1,2})-(\\d{3,4})-(\\d{4})";
		
		Pattern p = Pattern.compile(pattern);
		Matcher m = p.matcher(source);
		
		System.out.println("그룹의 개수 : " + m.groupCount());	//그룹화된 개수가 몇개인지 출력
		
		int i = 0;
		while(m.find()) {	// m의 요소가 있을 때까지
			System.out.println(++i + ": " + m.group() + " -> " + m.group(i));
			//System.out.println(++i + ": " + m.group() + " -> " + m.group(1) + "," + m.group(2) + "," + m.group(3));
		}
		
		
	}
}

 

실행결과

 

  • 패턴을 괄호 ( ) 로 이용해서 세 부분( 0\\d{1,2}  \\d{3,4}  \\d{4} 으로 나눴기 때문에  group(1)  group(3)  group(3) …으로 호출할 수 있다.( group(int i) 의  i 를 실제 그룹의 수를 넘어가는 수로 하게 되면 예외( java.lang.IndexOutOfBoundsException )가 발생한다.) 
  •  group( ) 이나  group(0) 은 그룹으로 매칭 된 문자열을 나눠지지 않은 전체로 반환한다.
  •  find( ) 는 주어진 소스에서 패턴과 일치하는 부분을 찾아내면  true 를 반환하고, 찾지 못하면 false 를 반환한다.  find( ) 를 호출해서 패턴과 일치하는 부분을 찾은 뒤에 다시 한번 find( ) 를 호출하면 이전에 발견한 패턴과 일치하는 부분의 다음부터 다시 패턴 매칭을 시작한다.

 

 

 

package Example;

import java.util.regex.*;	// Pattern과 Matcher가 속한 패키지

public class Example {
	public static void main(String[] args) {
		String source = "A broken hand works, but not broken heart.";
		String pattern = "broken";
		StringBuffer sb = new StringBuffer();
		
		Pattern p = Pattern.compile(pattern);
		Matcher m = p.matcher(source);
		System.out.println("source : " + source);	//변환되기 이전 문장 출력
		
		int i = 0;
		while(m.find()) {
        	//start()와 end()로 일치하는 부분의 위치를 알아낼 수 있다.
			System.out.println(++i + "번째 매칭 : " + m.start() + " ~ " + m.end());
			
			// broken을 drunken으로 치환하여 sb에 저장
			m.appendReplacement(sb, "drunken");
		}
		
		m.appendTail(sb);	//마지막으로 변환 위치 이후의 문장들을 붙여넣어준다.
		System.out.println("Replacement count : " + i);
		System.out.println("result : " + sb.toString());
	}
}

 Matcher 의  find( ) 로 정규식과 일치하는 부분을 찾으면,  start( ) 와  end( ) 로 위치를 찾아낼 수도 있고  appendReplacement(StringBuffer sb, String replacement) 를 이용해서 원하는 문자열( replacement )로 치환할 수도 있다. 치환된 결과는  StringBuffer 인  sb 에 저장된다. 

 


 sb에 저장되는 과정 

1. 문자열  source 에서  broken 을  m.find( ) 로 찾은 후 처음으로  m.appendReplacement(sb, "drunken"); 가 호출되면  source 의 시작부터  broken 을 찾은 위치까지의 내용에  drunken 을 더해서 저장한다.
sb에 저장된 내용 : "A drunken"

2.  m.find 는 첫 번째로 발견된 위치의 끝에서부터 다시 검색을 시작하여 두 번째  broken 을 찾게 된다. 다시  m.appendReplacement(sb, "drunken"); 가 호출되어 1의 과정 반복
sb에 저장된 내용 : "A drunken hand works, but not a drunken"

3.  m.appendTail(sb) 이 호출되면 마지막으로 치환된 이후의 부분을  sb 에 덧붙인다.
sb에 저장된 내용 : "A drunken hand works, but not a drunken heart."

 

 

 

 

 


java.util.Objects 클래스

   Object 클래스의 보조 클래스로  Math 클래스처럼 모든 메서드가  static 이다. 객체의 비교나 널 체크(null check)에 유용하다.  Object 클래스에는 두 객체의 등가 비교를 위한  equals( ) 만 있고, 대소 비교를 위한  compare( ) 가 없지만,  Objects 에서는  compare( ) 가 있다. 비교대상이 같으면  0 , 크면  양수 , 작으면  음수 를 반환한다. 또한  Objects 클래스에 있는  equals( ) 는  null 검사를 위한 조건식을 따로 넣지 않아도 되는 장점이 있다.  a 와  b 가 모두  null 일 경우에는 참을 반환한다. 

 

 

메서드 설  명
static boolean isNull(Object obj) 해당 객체가  null 인지 확인
(null이면 true를 반환, 아니면 false 반환)
static boolean nonNull(Object obj)  isNull( ) 과 반대 ( !Objects.isNull(obj) 와 같다)
static <T> T requireNonNull(T obj)
static <T> T requireNonNull(T obj, String message)
static <T> T requireNonNull(T obj, Supplier<String> messageSupplier)

 T 는  Object 타입을 의미한다
해당 객체가  null 이 아니여야 하는 경우에 사용, 2번째 매개변수로 지정하는 문자열은 예외 메시지가 됨
(만약 객체가  null 이면  NullPointerException 발생)

static int compare(Object a, Object b, Comparator c)

객체의 대소 비교
(같으면 0, 크면 양수, 작으면 음수를 반환)

static boolean equals(Object a, Object b)

  Object 클래스에 정의된  equals( ) 와 같지만  null 검사를 위한 조건식을 따로 넣지 않아도 된다(a와 b가 모두 null인 경우에는 참을 반환)

static boolean deepEquals(Object a, Object b)

객체를 재귀적으로 비교(다차원 배열의 비교 가능)

static String toString(Object o)
ststic String toString(Object o, String nullDefault)

 Object 클래스에 정의된  toString( ) 와 같지만 내부적으로  null 검사를 하기 때문에  null 검사를 위한 조건식을 따로 넣지 않아도 된다. ( nullDefault 는 o가 null일 때 대신 사용할 값)

static int hashCode(Object o)
static int hash(Object... values)
내부적으로  null 검사를 한 후  Object 클래스의  hashCode( ) 를 호출(단, null일때는 0을 반환)

 

 

 

package Example;

import java.util.*;
import static java.util.Objects.*;	//Objects클래스의 메서드를 static import

public class Example {
	public static void main(String[] args) {
		String[][] str2D_1 = new String[][] {{"aaa", "bbb"}, {"AAA", "BBB"}};
		String[][] str2D_2 = new String[][] {{"aaa", "bbb"}, {"AAA", "BBB"}};
		
		System.out.print("str2D_1 = {");		
		for(String[] tmp : str2D_1)	//str2D_1을 출력하기 위한 반복문
			System.out.print(Arrays.toString(tmp));
		System.out.println("}");
		
		System.out.print("str2D_2 = {");
		for(String[] tmp : str2D_1)	//str2D_1을 출력하기 위한 반복문
			System.out.print(Arrays.toString(tmp));
		System.out.println("}");
		
		System.out.println("equals(str2D_1, str2D_2) = " + Objects.equals(str2D_1, str2D_2));
		System.out.println("deepEquals(str2D_1. str2D_2) = " + Objects.deepEquals(str2D_1, str2D_2));
		
		System.out.println("isNull(null) = " + isNull(null));
		System.out.println("nonNull(null) = " + nonNull(null));
		System.out.println("hashCode(null) = " + Objects.hashCode(null));	//null일경우 0반환
		System.out.println("toString(null) = " + Objects.toString(null));
		System.out.println("toString(null, \"\") = " + Objects.toString(null, ""));
		
		Comparator c = String.CASE_INSENSITIVE_ORDER;	//대소문자 구분 없이 비교
		
		System.out.println("compare(\"aa\",\"bb\") = " + compare("aa","bb",c));
		System.out.println("compare(\"bb\",\"aa\") = " + compare("bb","aa",c));
		System.out.println("compare(\"ab\",\"AB\") = " + compare("ab","AB",c));
	}
}

 

실행결과

위의 표를 코드로 확인해보는 예제이다.  static import 문을 사용했음에도  Object 클래스의 메서드와 같은 것들은 컴파일러가 구별을 못한다.(충돌남) 이럴 때는 클래스의 이름을 붙여줄 수밖에 없다. 또한  String 클래스에 상수로 정의되어 있는  Comparator 가 있어서 이걸 이용해서  compare( ) 를 호출했다.  Comparator 는 문자열을 대소문자 구분 없이 비교할 때 사용하기 위한 것이다. 그래서  ab 와  AB 를 비교한 결과가  0 으로 같다고 나온다.

 

 

 

 

 


java.util.Scanner 클래스

Scanner(String source)
Scanner(File source)
Scanner(InputStream source)
Scanner(Readable source)
Scanner(ReadableByteChannel source)
Scanner(Path source)    // JDK 1.7부터 가능

 Scanner 클래스를 이용하면 화면, 파일, 문자열과 같은 입력 소스로부터 문자 데이터를 읽어올 수 있다.

  Scanner 에는 위의 표처럼 다양한 생성자를 지원하기 때문에 다양한 입력 소스로부터 데이터를 읽을 수 있다. 

 

 

Scanner useDelimiter(Pattern pattern)
Scanner useDelimiter(String pattern)

또한 정규식 표현(Regular expression)을 이용한 라인 단위의 검색을 지원하며 구분자(delimiter)에도 정규식 표현을 사용할 수 있어서 복잡한 형태의 구분자도 처리할 수 있다.  ( Scanner 말고도 입출력을 담당하는  java.io.Console 도 있지만 이클립스와 같은 IDE에서는 잘 동작하지 않기 때문에 거의  Scanner 를 사용한다.)

 

 

 

입력 변환

자료형 메서드
boolean nextBoolean( )
byte nextByte( )
short nextShort( )
int nextInt( )
long nextLong( )
double nextDouble( )
float nextFloat( )
String nextLine( )

 Scanner 에서는 자료형을 변환하는 메서드를 제공해주기 때문에 입력받은 문자열을 변환하는 과정이 비교적 간단하다.

 

 

package Example;

import java.util.*;	//Scanner 클래스를 사용하기 위한 import

public class Example {
	public static void main(String[] args) {
		Scanner s = new Scanner(System.in);
		String[] argArr = null;
		
		while(true) {
			String prompt = ">>";
			System.out.print(prompt);
			
			String input = s.nextLine();	//라인단위로 입력을 받는다
			
			input = input.trim();	//입력받은 값에서 앞뒤 공백 제거
			argArr = input.split(" +");	//공백이 여러개 일수도 있어서 정규식을 " +"로함(하나이상의 공백)
			
			String command = argArr[0].trim();	//공백으로 구분되는 첫번째 부분을 공백을 제거해서 command로 저장
			
		
			if("".equals(command)) continue;	//첫번째 부분이 없어도(공백으로 제거되어도) 계속 진행
			
			command = command.toLowerCase();	//명령어를 소문자로 변환(q나 Q를 똑같이 구분하기 위해서)
			
			if(command.equals("q")) {
				System.exit(0);
			}else {
				for(int i = 0 ; i < argArr.length ; i++)
					System.out.println(argArr[i]);
			}
		}
	}
}

 

실행결과

입력받은 내용을 공백을 구분자로 해서 출력하는 예제이다. 입력받은 라인의 단어는 공백이 여러 개일 수 있으므로  split 의 구분자로 정규식을 지정한다. 정규식의  " +" 는 하나이상의 공백을 의미한다. 그래서  tab 은 구분하지 못한다( 456 과  789 사이) 또한  argArr[0].trim( ) 을 사용했기 때문에 공백으로 구분되는 첫 번째 부분이  q 나  Q 가 되면 프로그램이 종료된다.( quit 같이 단독으로 들어가지 않는 것은 제외)

 

 

 

 

 


java.util.StringTokenizer 클래스

   StringTokenizer 는 긴 문자열을 구분자(delimiter)를 기준으로 토큰(token)이라는 여러 개의 문자열로 잘라내는 데 사용한다. 예를 들어  100,200,300,400 이라는 문자열을  , 를 구분자로 잘라내면  100  200  300  400 이라는 4개의 문자열(토큰)을 얻을 수 있다.  String 의  split(String regex) 이나  Scanner 의  useDelimiter(String pattern) 과 다른 점은 구분자로 단 하나의 문자밖에 사용할 수 없다는 것이다. 복잡한 형태의 구분자를 사용할 경우에는  split( ) 이나  useDelimiter( ) 와 같이 정규식을 사용하는 메서드를 사용해야 한다. 

 

 

package Example;

import java.util.*;	//StringTokenizer클래스를 사용하기 위한 import 

public class Example {
	public static void main(String[] args) {
		String expression = "x=100*(200*300)/2";
		StringTokenizer st = new StringTokenizer(expression, "+-*/=()", true);
		
		while(st.hasMoreTokens()) {
			System.out.println(st.nextToken());
		}
	}
}

 

구분자로 여러 문자들을 지정할 경우 모든 문자가 전부 구분자가 된다. 

 

 

 

StringTokenizer의 생성자와 메서드

생성자 / 메서드 설 명
StringTokenizer(String str, Strin delim) 문자열( str )을 지정된 구분자( delim )로 나누는  StringTokenizer 를 생성 (구분자는 토큰으로 간주되지 X)
StringTokenizer(String str, String delim, boolen returnDelims) 문자열( str )를 지정된 구분자( delim )로 나누는  StringTokenizer 를 생성.  returnDelims 의 값을 true로하면 구분자도 토큰으로 간주
int countTokens( ) 전체 토큰의 수를 반환
boolean hasMoreTokens( ) 토큰이 남아있는지 알려줌
String nextToken( ) 다음 토큰을 반환

 

 

 

StringTokenizer과 String클래스의 split( )

package Example;

import java.util.*;	//StringTokenizer클래스를 사용하기 위한 import 

public class Example {
	public static void main(String[] args) {
		String data = "100,,,200,300";
		
		String[] result = data.split(",");
		StringTokenizer st = new StringTokenizer(data, ",");
		
		for(int i = 0 ; i < result.length ; i++) {	// split()을 출력하기 위한 반복문
			System.out.print(result[i] + "|");
		}
		
		System.out.printf("%nsplit()의 개수 : " + result.length + "%n%n");
		
		int i = 0;
		for(; st.hasMoreElements() ; i++) {	// StringTokenizer을 출력하기 위한 반복문
			System.out.print(st.nextToken() + "|");
		}
		System.out.printf("%nStringTokenizer개수 : " + i);
	}
}

 

실행결과

구분자를  , 로 하는 문자열 데이터를  String 클래스의  split( ) 과  StringTokenizer 로 잘라낸 결과를 비교하는 예제이다.  split( ) 은 빈 문자열도 토큰으로 인식하는 반면  StringTokenizer 는 빈 문자열을 토큰으로 인식하지 않기 때문에 토큰 개수에 차이가 있는 것을 확인할 수 있다. 이외에도 성능의 차이가 있는데  split( ) 은 데이터를 토큰으로 잘라낸 결과를 배열에 담아서 반환하기 때문에 데이터를 토큰으로 바로바로 잘라서 반환하는  StringTokenizer 보다 성능이 떨어진다. 그러나 데이터의 양이 많은 경우가 아니라면 별 문제가 되지 않는다.

 

 

 

 

 


java.util.Random 클래스

double randNum = Math.random( );
double randNum = new Random( ).nextDouble( );

   Random 클래스를 사용하면 난수를 얻을 수 있다.  Math.random( ) 은 내부적으로  Random 클래스의 인스턴스를 생성해서 사용하므로 위의 두 문장은 같은 의미이다.  Math.random( ) 과  Random 의 가장 큰 차이점은  seed (종자값)값의 유무이다. 종자값이 같은  Random 인스턴스들은 항상 같은 난수를 같은 순서대로 반환한다.  seed 값은 난수를 만드는 공식에 사용되는 값으로 같은 공식에 같은 값을 넣으면 같은 결과를 얻는 것처럼 같은  seed 값을 넣으면 같은 난수를 얻게 된다. 

 

 

Random클래스의 생성자와 메서드

메서드 설  명
Random( )  System.currentTimeMillis( ) (현재시간)을  seed (종자값)로 이용하는  Random 인스턴스를 생성한다. (실행할 때마다 얻는 난수가 달라진다.)
Random(long seed) 매개변수  seed 를 종자값으로 하는  Random 인스턴스를 생성
boolean nextBoolean( )  boolean 타입의 난수 반환
void nextBytes(byte[ ] bytes)  bytes 배열에  byte 타입의 난수를 채워서 반환
double nextDouble( )  double 타입의 난수를 반환 ( 0.0 <= x < 1.0 )
float nextFloat( )  float 타입의 난수를 반환( 0.0 <= x < 1.0 )
double nextGaussian( ) 평균은  0.0 이고 표준편차는  1.0 인 가우시안(Gaussian)분포에 따른  double 형의 난수 반환
int nextInt( )  int 타입의 난수 반환(int의 범위)
int nextInt(int n)  0 ~ n 의 범위에 있는  int 값을 반환( n 은 범위에 포함되지 X)
long nextLong( )  long 타입의 난수를 반환(long의 범위)
void setSeed(long seed) 종자값을 주어진 값( seed )으로 변경

 

 

package Example;

import java.util.*;

public class Example {
	public static void main(String[] args) {
		Random rand1 = new Random(1);
		Random rand2 = new Random(1);
		
		System.out.println("== rand1 ==");
		for(int i = 0 ; i < 5 ; i++)
			System.out.println(i + " : " + rand1.nextInt());
		
		System.out.println();
		System.out.println("== rand2 ==");
		for(int i = 0 ; i < 5 ; i++)
			System.out.println(i + " : " + rand2.nextInt());
	}
}

 

실행결과

 seed 값이 같아서 같은 값들을 같은 순서로 얻는 것을 확인할 수 있다. 이처럼 같은 종자값을 갖는  Random 인스턴스는 시스템이나 실행시간 등에 관계없이 항상 같은 값을 같은 순서로 반환할 것을 보장한다.

 

 

 

+ Recent posts