본문 바로가기
Back-end

java.lang패키지와 유용한 클래스(3)

by 신재권 2021. 6. 25.

join()은 여러 문자열 사이에 구분자를 넣어서 결합한다. 구분자로 문자열을 자르는 split()과 반대의 작업을 생각하면 이해하기 쉽다.

String animal = "dog,cat,bear";
String[] arr =animals.split(",");  //문자열을 ','를 구분자로 나눠서 배열에 저장
String str = String.join("-", arr);  //배열의 문자열을 '-'로 구분해서 결합
System.out.println(str); //dog-cat-bear

java.util.StringJoiner 클래스를 사용해서 문자열을 결합할 수도 있는데, 사용하는 방법은 간단하다. 아래의 코드를 보는 것만으로 이해가 될 것이다.

StringJoiner sj = new StringJoiner(",", "[", "]");
Stirng[] strArr = {"aaa", "bbb", "ccc"};

for(String s : strArr)
	sj.add(s.toUpperCase());

System.out.println(sj.toString()); [AAA,BBB,CCC]
  • join()과 java.util.StringJoiner은 JDK 1.8부터 추가되었다.

유니코드의 보충문자

위의 표의 메서드 중에 매개변수의 타입이 char인 것들이 있고, int인 것들이 있따. 문자를 다루는 메서드라서 매개변수의 타입이 char일 것 같은데 왜 int일까? 그것은 확장된 유니코드를 다루기 위해서이다.

유니코드는 원래 2byte, 즉 16비트 문자체계인데, 이걸로도 모자라서 20비트로 확장하게 되었다. 그래서 하나의 문자를 char타입으로 다루지 못하고, int타입으로 다룰 수 밖에 없다. 확장에 의해 새로 추가된 문자들을 보충 문자(supplementary characters)라고 하는데, String클래스의 메서드 중에서는 보충 문자를 지원하는 것이 있고 지원하지 않는 것도 있다. 이들을 구별하는 방법은 쉽다. 매개변수가 'int ch'인 것들은 보충문자를 지원하는 것이고, 'char ch'인 것들은 지원하지 않는 것들이다. 보충문자를 사용하는 일은 거의 없기 때문에 이 정도만 알아두면 된다.

  • 확장된 유니코드(20bit) 가 적용된 것은 JDK1.5부터 이다.

문자 인코딩 변환

getBytes(String charsetName)를 사용하면, 문자열의 문자 인코딩을 다른 인코딩으로 변경할 수 있다. 자바가 UTF-16을 사용하지만, 문자열 리터럴에 포함되는 문자들은 OS의 인코딩을 사용한다. 한글 윈도우즈의 경우 문자 인코딩으로 CP949를 사용하며, UTF-8로 변경하려면 , 아래와 같이 한다.

  • 사용가능한 문자 인코딩 목록은 "System.out.println(java.nio.charset.Charset.availableCharsets()); 로 모두 출력이 가능하다.
byte[]  utf8_str = "가".getBytes("UTF-8");  //문자열을 UTF-8로 변환
String str = new String(utf8_str, "UTF-8");  //byte배열을 문자열로 변환

서로 다른 문자 인코딩을 사용하는 컴퓨터 간에 데이터를 주고받을 때는 적절한 문자 인코딩이 필요하다. 그렇지 않으면 알아볼 수 없는 내용의 문서를 보게될 것이다.

import java.io.UnsupportedEncodingException;
import java.util.StringJoiner;


public class StringEx5 {

	public static void main(String[] args) {
		String str ="가";
		try {
			byte[] bArr = str.getBytes("UTF-8");
			byte[] bArr2 = str.getBytes("CP949");
			System.out.println("UTF-8 : "+joinByteArr(bArr));
			System.out.println("CP949 : "+joinByteArr(bArr2));
			
			System.out.println("UTF-8 : "+new String(bArr,"UTF-8"));
			System.out.println("CP949 : "+new String(bArr,"CP949"));
			
			
		} catch (UnsupportedEncodingException e) {
			
			e.printStackTrace();
		}
		
		

	}
	
	static String joinByteArr(byte[] bArr){
		StringJoiner sj = new StringJoiner(":", "[","]");
		
		for(byte b : bArr)
			sj.add(String.format("%02X", b));
		return sj.toString();
	}

}
==================
UTF-8 : [EA:B0:80]
CP949 : [B0:A1]
UTF-8 : 가
CP949 : 가

UTF-8은 한글 한 글자를 3byte로 표현하고, CP949는 2byte로 표현한다. 그래서 문자 '가'는 UTF-8로 '0xEAB080'이고, CP949로 '0xB0A1"이다.

  • 문자 인코딩 CP949는 MS949 라고도 한다.

String.format()

format()은 형식화된 문자열을 만들어내는 간단한 방법이다. printf()하고 사용법이 완전히 똑같으므로 사용하는데 별 어려움은 없을 것이다.

String str = String.format("%d더하기 %d는 %d입니다.", 3,5,3+5);
System.out.println(str);  //3 더하기 5는 8입니다.

기본형 값을 String으로 변환

숫자로 이루어진 문자열을 숫자로, 또는 그 반대로 변환하는 경우가 자주 있다. 이미 배운것과 같이 기본형을 문자열로 변경하는 방법은 간단하다. 숫자에 빈 문자열 ""을 더해주기만 하면된다. 이 외에도 valueOf()를 사용하는 방법도 있다. 성능은 valueOf()가 더 좋지만, 빈 문자열을 더하는 방법이 간단하고 편하기 때문에 성능향상이 필요한 경우에만 valueOf()를 쓰자

int i = 100;
String str1 =i +"";   //100을 "100"으로 변환하는 방법1
String str2 = String.valueOf(i);  //100을 "100"으로 변환하는 방법2
  • 변수에 String을 더하면 참조변수가 가리키는 인스턴스의 toString()을 호출하여 String을 얻은 다음 결합한다.

String을 기본형 값으로 변환

반대로 String을 기본형으로 변환하는 방법도 간단하다. valueOf()를 쓰거나 앞서 배운 parseInt()를 사용하면 된다.

int i = Integer.parseInt("100");  //"100"을 100으로 변환하는 방법1
int i2 = Integer.valueOf("100");  //"100"을 100으로 변환하는 방법2

원래 valueOf()의 반환 타입은 int가 아니라 Integer인데 , 오토박싱(auto-boxing)에 의해 Integer가 int로 자동 변환된다.

Integer i2 = Integer.valueOf("100"); //원래는 반환 타입이 Integer

예전에는 parseInt()와 같은 메서드를 많이 썻는데, 메서드의 이름을 통일하기 위해 valueOf()가 나중에 추가되었다. valueOf(String s)는 메서드 내부에서 그저 parseInt(String s)를 호출할 뿐이므로, 두 메서드는 반환 타입만 다르지 같은 메서드 이다.

public static Integer valueOf(String s) throws NumberFromatException{
		return Integer.valueOf(parseInt(s,10));  //여기서 10은 10진수를 의미
}
  • parseInt(s, 10)은 parseInt(s)와 같다.

기본형과 문자열간의 변환방법을 정리하면 다음과 같다.

기본형→ 문자열

String String.valueOf(boolean b) String String.valueOf(char c) String String.valueOf(int i) String String.valueOf(long l) String String.valueOf(float f) String String.valueOf(double d)

문자열 → 기본형

boolean Boolean.parseBoolean(String s)

byte Byte.parseByte(String s)

short Short.parseShort(String s)

long Long.parseLong(String s)

float Float.parseFloat(String s)

double Double.parseDouble(String s)

  • byte, short을 문자열로 변경할 때는 String valueOf(Int i)를 사용하면 된다.
  • 문자열 "A"를 문자'A'로 변환하려면 char ch = "A".charAt(0); 과 같이 하면 된다.

위의 표에 있는 메서드만 알고 있으면 문자열과 기본형 값의 변환에는 아무런 문제가 없을 것이다. 이 변환은 프로그래밍에서 반드시 알고 있어야 하는 아주 중요한 내용이다.

Boolean, Byte와 같이 기본형 타입의 이름의 첫 글자 대문자인 것은 래퍼 클래스(wrapper class)이다. 기본형 값을 감싸고 있는 클래스라는 뜻에서 붙여진 이름으로 기본형을 클래스로 표현한 것이다.

public class StringEx6 {

	public static void main(String[] args) {
		int iVal = 100;
		String strVal = String.valueOf(iVal); //int를 String으로 변환한다.
		
		double dVal = 200.0;
		String strVal2 = dVal +"";  //int를 String으로 변환하는 또 다른 방법
		
		double sum =Integer.parseInt("+"+strVal)+Double.parseDouble(strVal2);
		double sum2 = Integer.valueOf(strVal) + Double.valueOf(strVal2);
		
		System.out.println(String.join("",strVal,"+",strVal2,"=")+sum);
		System.out.println(strVal+"+"+strVal2+"="+sum2);
	}

}
========================
100+200.0=300.0
100+200.0=300.0

이 예제는 문자열과 기본형간의 변환의 예를 보여준다. parseInt()나 parseFloat() 같은 메서드는 문자열에 공백 또는 문자가 포함되어 있는 경우 변환 시 예외(NumberFormatException)가 발생할 수 있으므로 주의해야 한다. 그래서 문자열 약끝에 공백을 제거해주는 trim()을 습관적으로 같이 사용하기도 한다.

int val =Integer.parseInt(" 123 ".trim()); //문자열의 양 끝의 공백을 제거 후 변환

그러나 부호를 의미하는 '+'나 소수점을 의미하는 '.'와 float형 값을 뜻하는 f와 같은 자료형 접미사는허용된다. 단 자료형에 알맞은 변환을 하는 경우에만 허용된다.

만일 '1.0f'를 int형 변환 메서드인 Integer>parseInt(String s)를 사용해서 변환하려하면 예외가 발생하지만, Float.parseFloat(String s)를 사용하면 아무런 문제가 없다.

이처럼 문자열을 숫자로 변환하는 과정에서는 예외가 발생하기 쉽기 때문에 주의를 기울여야 하고 , 예외가 발생했을 떄의 처리를 적절히 해주어야 한다.

  • '+'가 포함된 문자열이 parseInt()로 변환가능하게 된것은 JDK1.7부터이다.
  • Integer클래스의 static int parseInt(String s, int radix)를 사용하면 16진수 갑승로 표현된 문자열도 변환할 수 있기 때문에 대소문자 구별 없이 a,b,c,d,e,f도 사용할 수 있다. int result = Integer.parseInt("a",16)의 경우 result에는 정수 값 10이 저장된다.(16진수 a는 10진수로 10을 뜻한다.)
public class StringEx7 {

	public static void main(String[] args) {
		String fullName = "Hello.java";
		
		//fullName에서 '.'의 위치를 찾는다.
		int index = fullName.indexOf('.');
		
		//fullName의 첫번쨰 글자부터 '.'이 있는 곳까지 문자열을 추출한다.
		String fileName = fullName.substring(0, index);
		
		// '.'의 다음 문자 부터 시작해서 문자열의 끝까지 추출한다.
		// fullName.substring(index+1, fullName.length()); 의 결과와 같다.
		String ext = fullName.substring(index+1);
		
		System.out.println(fullName + "의 확장자를 제외한 이름은 "+fileName);
		System.out.println(fullName + "의 확장자는 "+ext);

	}

}
---------------------------
Hello.java의 확장자를 제외한 이름은 Hello
Hello.java의 확장자는 java

위 예제는 substring메서드를 이용하여 한 문자열에서 내용의 일부를 추출하는 예를 보이는 것이다. substring(int start, int end)를 사용할 떄 주의해야 할 점은 매개변수로 사용되는 문자열에서 각 문자의 위치를 뜻하는 index가 0부터 시작한다는 것과 start부터 en의 범위 중 end 위치에 있는 문자는 결과에 포함되지 않는 것이다.

  • end에서 start값을 빼면 substring에 의해 추출될 글자의 수가 된다.
  • substring의 철자에 주의하도록한다. subString이 아니다.
String str = "Hello.java";
int index = str.indexOf('.'):
int index2 = str.lastIndexOf('.');
String a = str.substring(0,5);
String b =str.substring(6,10);
============================
index = 5
index2 =5
a = "Hello"
b ="java"