형식화 클래스
성적처리 프로그램을 작성했을 때 각 점수의 평균을 소수점 2자리로 일정하게 맞춰서 출력하려면 어떻게 해야 할까 고민해본 적이 있을 것이다.
평균값에 100을 곱하고 int형으로 형변환한 다음에 다시 100f로 나누고 반올림하려면 Math.round()도 써야하고 등등 생각만 해도 머리가 복잡하다.
날짜를 형식에 맞게 출력하려면 숫자보다 더 복잡해진다. Calendar를 이용해서 년, 월, 일, 시 , 분, 초를 각각 별도로 얻어서 조합을 해야 하는 과정을 거쳐야 한다.
자바에서는 이러한 문제들을 쉽게 해결할 수 있는 방법을 제공하는데, 그것이 바로 형식화 클래스이다. 이 클래스는 java.text패키지에 포함되어 있으며, 숫자, 날자, 텍스트 데이터를 일정한 형식에 맞게 표현할 수 있는 방법을 객체지향적으로 설계하여 표준화하였다.
형식화 클래스는 형식화에 사용될 패턴을 정의하는데, 데이터를 정의된 패턴에 맞춰 형식화할 수 있을 뿐만 아니라 역으로 형식화된 데이터에서 원래의 데이터를 얻어낼 수도 있다.
이것은 마치 "123"과 같은 문자열을 Integer.parseInt()를 사용해서 123이라는 숫자로 변환하는 것과 같은 일이 가능하다는 것을 의미한다. 즉 형식화된 데이터의 패턴만 정의해주면 복잡한 문자열에서도 substring()을 사용하지 않고도 쉽게 원하는 값을 얻어낼 수 있다는 것이다.
이 외에도 형식화 클래스는 알아두면 편리하게 사용할 좋은 기능들을 가지고 있는데 백문이 불여일견이라고 긴 설명보다도 예제를 통해서 어떻게 활용하는지 이해하는 쪽이 훨 씬 더 빠르리라 생각한다.
DecimalFormat
형식화 클래스 중에서 숫자를 형식화 하는데 사용되는 것이, DecimalFormat이다.
DecimalFormat을 이용하면 숫자 데이터를 정수, 부동소수점, 금액 등의 다양한 형식으로 표현할 수 있으며, 반대로 일정한 형식의 텍스트 데이터를 숫자로 쉽게 변환하는 것도 가능하다.
형식화 클래스에서는 원하는 형식이 표현 또는 변환하기 위해서 패턴을 정의하는데, 형식화 클래스에서는 패턴을 정의하는 것이 전부라고 해도 과언이 아니다.
아래의 표에 DecimalFormat의 패턴의 작성에 사용되는 기호와 설명, 그리고 자주사용될 만한 패턴들을 예로 들었다. 여기에 제시된 패턴들만 잘 이해하고 응용하면 DecimalFormat의 패턴을 작성하는 것은 그리 어려운 일이 아니다.
기호 | 의미 | 패턴 | 결과 |
0 | 10진수(값이 없을 때는 0) | 0 0.0 00000000000.0000 |
1234568 123457.9 0001234567.8900 |
# | 10진수 | # #.# ##########.#### |
1234568 123457.9 1234567.8900 |
. | 소수점 | #.# | 1234567.9 |
, | 단위 구분자 | #,###,## #,####,## |
1,123,567,89 123,4567,89 |
- | 음수부호 | #.#- -#.# |
1234567.9- -1234567.9 |
E | 지수기호 | #E0 0E0 ##E0 00E0 ####E0 0000E0 #.#E0 0.0E0 0.000000000000E0 00.00000000000E0 000.0000000000E0 #.########E0 ##.#######E0 ###.######E0 |
.1E7 1E6 1.2E6 12E5 123.5E4 1235E3 1.2E6 1.2E6 1.234567890E6 12.34567890E5 123.4567890E4 1.23456789E6 1.23456789E6 1.23456789E6 |
; | 패턴구분자 | #,###,##+;#,###,##- | 1,234,567,89+(양수일때) 1,234,567,89-(음수일때) |
% | 퍼센트 | #.#% | 123456789% |
\u2030 | 퍼밀(퍼센트x10) | #.#\u2030 | 1234567890‰ |
\u00A4 | 통화 | \u00A4 #,### | ₩ 1,234,568 |
' | escape문자 | '#'#,### "#,### | #1,234,568 '1,234,568 |
DecimalFormat을 사용하는 방법은 간단하다. 먼저 원하는 출력형식의 패턴을 작성하여 DecimalFormat인스턴스를 생성한다음, 출력하고자 하는 문자열로 format메서드를 호출하면 원하는 패턴에 맞게 변환된 문자열을 얻게 된다.
double number = 1234567.89;
DecimalFormat df = new DecimalFormat("#.#E0");
String result= df.format(number);
import java.text.*;
public class DecimalFormatEx1 {
public static void main(String[] args) {
double number= 1234567.89;
String[] pattern ={
"0",
"#",
"0.0",
"#.#",
"0000000000.0000",
"#.#-",
"-#.#",
"#,###.##",
"#,####.##",
"#E0",
"0E0",
"##E0",
"####E0",
"0000E0",
"#.#E0",
"0.0E0",
"0.00000000E0",
"00.0000000E0",
"000.000000E0",
"#.#########E0",
"##.########E0",
"###.#######E0",
"#,###.##+;#,###.##-",
"#.#%",
"#.#\u2030",
"\u00A4 #,###",
"'#'#,###",
"''#,###",
};
for(int i=0; i<pattern.length;i++){
DecimalFormat df = new DecimalFormat(pattern[i]);
System.out.printf("%19s : %s\n",pattern[i],df.format(number));
}
}
}
========================
0 : 1234568
# : 1234568
0.0 : 1234567.9
#.# : 1234567.9
0000000000.0000 : 0001234567.8900
#.#- : 1234567.9-
-#.# : -1234567.9
#,###.## : 1,234,567.89
#,####.## : 123,4567.89
#E0 : .1E7
0E0 : 1E6
##E0 : 1.2E6
####E0 : 123.5E4
0000E0 : 1235E3
#.#E0 : 1.2E6
0.0E0 : 1.2E6
0.00000000E0 : 1.23456789E6
00.0000000E0 : 12.3456789E5
000.000000E0 : 123.456789E4
#.#########E0 : 1.23456789E6
##.########E0 : 1.23456789E6
###.#######E0 : 1.23456789E6
#,###.##+;#,###.##- : 1,234,567.89+
#.#% : 123456789%
#.#‰ : 1234567890‰
¤ #,### : ₩ 1,234,568
'#'#,### : #1,234,568
''#,### : '1,234,568
자주 사용될 만한 패턴들을 작성해서 테스트한 예제이다. 각 패턴에 의한 결과를 비교해보고 이 패턴들을 변형하여 새로운 패턴을 만들어 테스트 해보자.
import java.text.DecimalFormat;
public class DecimalFormatEx2 {
public static void main(String[] args) {
DecimalFormat df = new DecimalFormat("#,###.##");
DecimalFormat df2 = new DecimalFormat("#.###E0");
try{
Number num = df.parse("1,234,567,89");
System.out.print("1,234,567,89"+" -> ");
double d =num.doubleValue();
System.out.print(d+ " -> ");
System.out.println(df2.format(num));
}catch(Exception e){}
}
}
---------------------
1,234,567,89 -> 1.23456789E8 -> 1.235E8
패턴을 이용해서 숫자를 다르게 변환하는 예제이다. parse메서드를 이용하면 기호와 문자가 포함된 문자열을 숫자로 쉽게 변환할 수 있다.
- Integer.parseInt메서드는 콤마(,)가 포함된 문자열을 숫자로 변환하지 못한다.
parse(String source)는 DecimalFormat의 조상인 NumberFormat에 정의된 메서드이며, 이 메서드의 선언부는 다음과 같다.
public Number parse(String source) throw ParseException
Number클래스는 Integer, Double가 같은 숫자를 저장하는 래퍼 클래스의 조상이며, doubleValue()는 Number에 저장된 값을 doulbe형의 값으로 변환하여 반환한다. 이 외에도 intValue(), floatValue()등의 메서드가 Number클래스에 정의되어 있다.
SimpleDateFormat
앞에서 날짜를 계산할 때 Date와 Calendar를 사용해서 날짜를 계산하는 방법을 배웠는데, 이제는 출력하는 방법에 대해서 배울차례이다.
Date와 Calendar만으로 날짜 데이터를 원하는 형태로 다양하게 출력하는 것은 불편하고 복잡하다. 그러나 SimpleDateFormat을 사용하면 이러한 문제들이 간단히 해결된다.
- DateFormat은 추상클래스로 SImpleDateFormat의 조상이다. DateFormat는 추상클래스이므로 인스턴스를 생성하기 위해서는 getDateInstace()와 같은 static메서드를 이용해야 한다. getDateInstance()에 의해서 반환되는 것은 DateFormat을 상속받아 완전하게 구현한 SimpleDateFormat인스턴스이다.
기호 | 의미 | 보기 |
G | 연대(BC,AD) | AD |
y | 년도 | 2006 |
M | 월(1~12또는 1월~12월) | 10또는 10월, OCT |
W | 월의 몇 번째 주(1~5) | 4 |
D | 년의 몇 번째 일(1~366) | 100 |
d | 월의 몇 번째 일(1~31) | 15 |
F | 월의 몇 번째 요일(1~5) | 1 |
E | 요일 | 월 |
a | 오전/오후(AM,PM) | PM |
H | 시간(0~23) | 20 |
k | 시간(1~24) | 13 |
K | 시간(0~11) | 10 |
h | 시간(1~12) | 11 |
m | 분(0~59) | 35 |
s | 초(0~59) | 55 |
S | 천분의 일초(0~999) | 253 |
z | Time zone(Genral time zone) | GMT+9:00 |
Z | Time zone(RFC 822 time zone) | +0900 |
' | escape문자 (특수문자를 표현하는데 사용) | 없음 |
SimpleDateFormat을 사용하는 방법은 간단하다. 먼저 원하는 출력 형식의 패턴을 작성하여 SimpleDateFormat인스턴스를 생성한 다음, 출력하고자 하는 Date인스턴스를 가지고 format(Date d)를 호출하면 지정한 출력형식에 맞게 변환된 문자열을 얻게된다.
Date today =new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
//오늘 날짜를 yyyyy-MM-dd형태로 변환하여 반환한다.
String result = df.format(today)
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatEx1 {
public static void main(String[] args) {
Date today = new Date();
SimpleDateFormat sdf1, sdf2, sdf3, sdf4;
SimpleDateFormat sdf5, sdf6, sdf7, sdf8, sdf9;
sdf1 = new SimpleDateFormat("yyyy-MM-dd");
sdf2 = new SimpleDateFormat("''yy년 MMM dd일 E요일");
sdf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
sdf4 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss a");
sdf5 = new SimpleDateFormat("오늘은 올 해의 D번째 날입니다.");
sdf6 = new SimpleDateFormat("오늘은 이 달의 d번째 날입니다.");
sdf7 = new SimpleDateFormat("오늘은 올해의 w번째 주입니다.");
sdf8 = new SimpleDateFormat("오늘은 이 달의 W번쨰 주입니다.");
sdf9 = new SimpleDateFormat("오늘은 이 달의 F번째 E요일입니다.");
System.out.println(sdf1.format(today));
System.out.println(sdf2.format(today));
System.out.println(sdf3.format(today));
System.out.println(sdf4.format(today));
System.out.println();
System.out.println(sdf5.format(today));
System.out.println(sdf6.format(today));
System.out.println(sdf7.format(today));
System.out.println(sdf8.format(today));
System.out.println(sdf9.format(today));
}
}
========================
2021-07-03
'21년 7월 03일 토요일
2021-07-03 15:51:15.525
2021-07-03 03:51:15 오후
오늘은 올 해의 184번째 날입니다.
오늘은 이 달의 3번째 날입니다.
오늘은 올해의 27번째 주입니다.
오늘은 이 달의 1번쨰 주입니다.
오늘은 이 달의 1번째 토요일입니다.
자주 사용될만한 패턴을 만들어서 다양한 형식으로 예제가 실행된 날짜와 시간을 출력해보았다. 이 예제에 사용된 패턴들을 다양하게 응용하여 테스트 하자.
- 홑따옴표('')는 escape기호기 때문에 패턴 내에서 홑따옴표를 표시하기 위해서는 홑따옴표를 연속적으로 두 번 사용해야 한다.
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class DateFormatEx2 {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
cal.set(2005,9,3); // 2005년 10월 3일
Date day = cal.getTime(); //Calendar를 Date로 변환
SimpleDateFormat sdf1, sdf2, sdf3, sdf4;
sdf1 = new SimpleDateFormat("yyyy-MM-dd");
sdf2 = new SimpleDateFormat("yy-MM-dd E요일");
sdf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
sdf4 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss a");
System.out.println(sdf1.format(day));
System.out.println(sdf2.format(day));
System.out.println(sdf3.format(day));
System.out.println(sdf4.format(day));
}
}
===============
2005-10-03
05-10-03 월요일
2005-10-03 15:57:43.936
2005-10-03 15:57:43 오후
Date인스턴스만 format메서드에 사용될 수 있기 때문에 Calendar인스턴스를 Date인스턴스로 변환하는 방법을 보여주는 예제이다. Date인스턴스와 Calendar인스턴스간의 변환방법을 잘 정리하자.
- Date인스턴스를 Calendar인스턴스로 변환할 때는 Calendar클래스의 setTime()을 사용하면 된다.
import java.util.*;
import java.text.*;
public class DateFormatEx3 {
public static void main(String[] args) {
DateFormat df = new SimpleDateFormat("yyyy년 MM월 dd일");
DateFormat df2 = new SimpleDateFormat("yyyy/MM/dd");
try{
Date d =df.parse("2015년 11월 23일");
System.out.println(df2.format(d));
}catch(Exception e){}
}
}
---------------------------------
2015/11/23
parse(String source)를 사용하여 날짜 데이터의 출력형식을 변환하는 방법을 보여주는 예제이다. Integer의 parseInt()가 문자열을 정수로 변환하는 것처럼 SimpleDateFormat의 parse(String source)는 문자열 source을 날짜Date인스턴스로 변환해주기 때문에 매우 유용하게 쓰일 수 있다.
예를 들어 사용자로부터 날짜 데이터를 문자열로 입력받았을 때, 입력받은 문자열을 날짜로 인식하기 위해서는 substring메서드를 이용해서 년,월,일을 뽑아내야 하는데, parse(String source)은 이러한 수고를 덜어준다.
- parse(String source)는 SimpleDateFormat의 조상인 DateFormat에 정의되어 있다.
- 지정된 형식과 입력된 형식이 일치하지 않는 경우에는 예외가 발생하므로 적절한 예외처리가 필요하다.
import java.util.*;
import java.text.*;
public class DateFormatEx4 {
public static void main(String[] args) {
String pattern ="yyyy/MM/dd";
DateFormat df = new SimpleDateFormat(pattern);
Scanner s = new Scanner(System.in);
Date inDate =null;
System.out.println("날짜를 "+pattern+"의 형태로 입력해주세요. (입력예 :2015/12/31)");
while(s.hasNextLine()){
try{
inDate = df.parse(s.nextLine());
break;
}catch(Exception e){
System.out.println("날짜를 "+pattern+"의 형태로 입력해주세요. (입력예 :2015/12/31)");
}
}
Calendar cal = Calendar.getInstance();
cal.setTime(inDate);
Calendar today = Calendar.getInstance();
long day = (today.getTimeInMillis()-cal.getTimeInMillis()) / (60*60*1000);
System.out.println("입력하신 날짜는 현재와 "+day+"시간 차이가 있습니다.");
}
}
============================
날짜를 yyyy/MM/dd의 형태로 입력해주세요. (입력예 :2015/12/31)
asd
날짜를 yyyy/MM/dd의 형태로 입력해주세요. (입력예 :2015/12/31)
20151231
날짜를 yyyy/MM/dd의 형태로 입력해주세요. (입력예 :2015/12/31)
2015/12/31
입력하신 날짜는 현재와 48280시간 차이가 있습니다.
화면으로부터 날짜를 입력받아서 계산결과를 출력하는 예제이다. while과 try-catch문을 이용해서 사용자가 올바른 형식으로 날짜를 입력할 때 까지 반복해서 입력받도록 하였다.
지정된 패턴으로 입력되지 않은 경우 parse메서드를 호출하는 부분에서 예외(ParseException)가 발생하기 때문에 while문을 벗어나지 못한다.
ChoiceFormat
ChoiceFormat은 특정 범위에 속하는 값을 문자열로 반환해준다. 연속적 또는 불연속적인 범위의 값들을 처리하는 데 있어서 if문이나 switch문은 적절하지 못한 경우가 많다.
이럴 때 ChoiceFormat을 잘 사용하면 복잡하게 처리될 수 밖에 없었던 코드를 간단하고 직관적으로 만들 수 있다.
import java.text.*;
public class ChoiceFormatEx1 {
public static void main(String[] args) {
double[] limits = {60,70,80,90}; //낮은 값부터 큰 값의 순서로 적어야 한다.
//limits, grades간의 순서와 개수를 맞추어야 한다.
String[] grades = {"D","C","B","A"};
int[] scores = {100,95,88,70,52,60,70};
ChoiceFormat form = new ChoiceFormat(limits, grades);
for(int i=0; i<scores.length;i++){
System.out.println(scores[i]+" : "+form.format(scores[i]));
}
}
}
=======================
100 : A
95 : A
88 : B
70 : C
52 : D
60 : D
70 : C
두 개의 배열이 사용되었는데 하나(limits)는 범위의 경계값을 저장하는데 사용하엿고, 또 하나(grades)는 범위에 포함된 값을 치환할 문자열을 저장하는데 사용되었다.
경계값은 double 형으로 반드시 모두 오름차순으로 정렬되어 있어야 하며, 치환 될 문자열의 개수는 경계값에 의해 정의된 범위의 개수와 일치해야 한다. 그렇지 않으면 IllegalArgumentException이 발생한다.
예제에서는 4개의 경계값에 의해 '60~69', '70~79', '80~89', '90~'의 범위가 정의되어 있다.
import java.text.*;
public class ChoiceFormatEx2 {
public static void main(String[] args) {
String pattern = "60#D|70#C|80<B|90#A";
int[] scores= {91,90,80,88,70,52,60};
ChoiceFormat form = new ChoiceFormat(pattern);
for(int i=0; i< scores.length;i++){
System.out.println(scores[i]+":"+form.format(scores[i]));
}
}
}
==================
91:A
90:A
80:C
88:B
70:C
52:D
60:D
이전 예제를 패턴을 사용하도록 변경한 것이다. 이처럼 배열 대신 패턴을 사용해서 보다 간결하게 처리할 수도 있다. 패턴은 구분자로 '#'와 '<'두 가지를 제공하는데 'limit#value'의 형태로 사용된다. '#'는 경계값을 범위에 포함시키지만, '<'는 포함시키지 않는다. 위의 결과에서는 90은 A지만, 80은 B가 아닌, C인것에 주목해야 한다.
경계값을 포함하지 않는다고 해서 "61#D|71#C|81#B|91#A"와 같이 하는것 보다는 , "60<D|70<C|80<B|90<A"과 같이 하는 것이 낫다.
'Back-end' 카테고리의 다른 글
10 날짜와 시간 & 형식화 (4) (0) | 2021.07.05 |
---|---|
01 데이터 베이스 (0) | 2021.07.04 |
10 날짜와 시간 & 형식화 (2) (0) | 2021.07.02 |
10 날짜와 시간 & 형식화(1) (0) | 2021.07.02 |
java.lang패키지와 유용한 클래스(8) (0) | 2021.07.01 |