본문 바로가기

JAVA/JAVA 기초

문자열관련

반응형

문자열


String 클래스

String은 문자열의 정보를 내부에서 char타입의 배열로 유지하고 있다.

char는 단일문자를 보관하는 타입인데, String은 char를 배열로 유지함으로써 여러문자을 모아서 처리할 수 있다.


String 객체는 한번 만들면 변경할 수 없다.

String originalText = "ABC";

String lowerText = originalText.toLowerCase();


String 클래스는 어떠한 가공메서드를 호출해도 원래 문자열은 변경되지 않는다.

불변객체(Immutable)


문자열 합치기


1.StringBuilder 클래스

가변 문자열을 유지하는 클래스로 문자열의 결합이나 삭제 등을 반복할때 사용.

StringBuilder bulider = new StringBuilder();


String a = "a";

String b = "b";

StringBuilder bulider = new StringBuilder();

bulider.append(a);

bulider.append(b);


String str = bulider.toString();

System.out.println(str);


2.+연산자

String ab = a+b;

System.out.println(ab);



3.String클래스의 concat 메서드

String c = "c";

String d = "d";

System.out.println(c.concat(d));




+연산자의 경우 컴파일러에서 코드가 변환된다.

String str = "";

for(int i=0; i<100000; i++) {

str += "a";

}



String str = "";

for(int i=0; i<100000; i++) {

str = new StringBuilder(String.valueOf(str)).append("a").toString();

}


StringBuilder 클래스에서도 문자열을 배열로 보유하고 있지만, 미리 여유 있는 크기의 배열을 확보하고 있기 때문에 append 메서드에 의한 문자열 추가에서 매번 새로운 배열을 만들일은 없다.


로컬변수 등 여러 스레드로부터 액세스되지 않는 경우

-StringBuilder

여러 스레드로부터 사용되는경우

-StringBuffer


문자열 분할

String content = "Hello World!";

String[] words = content.split(" ");


for(String word : words) {

System.out.println(word);

}


split 메서드는 분할할 구분문자를 정규표현으로 지정할 수 있다.


String url = "www.google.co.kr";

String[] words = url.split("\\.");

for(String word : words) {

System.out.println(word);

}


여러문자열 결합

List<String> strArray = new ArrayList<>();

strArray.add("I");

strArray.add("love");

strArray.add("you");


String message = String.join(" ", strArray);

System.out.println(message);


String message = String.join(".","www","naver","com");

System.out.println(message);


문자열 치환

String content1 = "I love you";

String replaceContent = content1.replace("love", "like");

System.out.println(replaceContent);


문자열검색

String content1 = "I love you";

int index = content1.indexOf("love");

System.out.println(index);


int index1 = content1.indexOf("like");

System.out.println(index1);


int index2 = content1.indexOf("o", 4);

System.out.println(index2);


int index3 = content1.lastIndexOf("o");

System.out.println(index3);


정규표현

Pattern pattern = Pattern.compile("This is a .*\\."); //정규표현의 패턴을 생성


String book = "This is a pen."; //정규표현의 패턴에 적합한지를 체크하는 문자열


Matcher matcher = pattern.matcher(book); //정규표현 처리를 실시하기 위한 클래스를 취득


if(matcher.matches()) { //정규표현의 패턴에 적합한지를 체크

System.out.println("적합");

} else {

System.out.println("비적합");

}


Pattern pattern1 = Pattern.compile("\\s+"); //정규표현의 패턴을 생성('\\s'는 공백을 의미)


String book1 = "This           is a pen."; //정규표현의 패턴에 적합한지를 체크하는 문자열


String[] words2 = pattern1.split(book1);


for(String word : words2) {

System.out.println(word);

}



Pattern pattern2 = Pattern.compile("\\s+"); //정규표현의 패턴을 생성('\\s'는 공백을 의미)


String book2 = "This           is a pen."; //정규표현의 패턴에 적합한지를 체크하는 문자열


Matcher matcher2 = pattern2.matcher(book2); //정규표현 처리를 실시하기 위한 클래스를 취득


System.out.println(matcher2.replaceAll(" "));



String book3 = "This           is a pen."; //정규표현의 패턴에 적합한지를 체크하는 문자열


System.out.println(book3.matches("Th.* is a .*\\."));


String[] words3 = book3.split("\\s+");

for(String word : words3) {

System.out.println(word);

}


System.out.println(book3.replaceAll("\\s+", " "));


실제로 정규표현을 이용한 처리를 한 번만 사용할 경우는 String클래스를 메서드를 이용하면 충분하다.

그러나 String클래스의 메서드 내부에서는 실제로 정규 표현 클래스를 이용한 처리가 이루어지고 있다는 점에 유의해야한다.

몇번이고 동일한 정규표현을 이용하여 처리하는 경우, String 클래스의 메서드에서는 그때마다 Pattern클래스와 Matcher클래스의 객체가 생성되어 처리가 늦어진다.

Pattern클래스의 객체를 생성할때는 정규 표현 패턴을 컴퓨터가 처리하기 쉽게 변환하기 때문에 시간이 걸린다.


한번만 문자열을 처리를 할 경우(애플리케이션을 시작할 때의 인수처리 등)

-String 클래스를 사용

대량의 문자열을 반복하는경우(로그파일의 처리등)

-Pattern 클래스의 객체르르 생성하고 객체를 재사용한다.


문자열 포맷과 출력

문자열 출력에는 System.out.println, System.err.println 메서드가 있다.

출력할 문자열의 일부를 매개변수로 하여 실행할 때 마다 출력할 문자열의 일부를 변경한다식의 사용법도 자주 필요하다.

printf메서드


int number =13;

String parameter ="apples";


System.out.printf("I have %d %s", number, parameter);


System.out.printf("I have %X %S", number, parameter); //%X는 16진수


MessageFormat

매개변수를 적용한 출력 문자열을 String으로 취득하길 원할 경우 MessageForamt 클래스의 foramt 메서드를 사용한다.


String message = MessageFormat.format("I have {0}, {1}", number, parameter);




문자코드

문자코드

자바는 내부적으로 UTF-16으로 인코딩된 Unicode를 사용하며 문자열을 보관하고 있다.

자바의 String 클래스는 내부적으로 char배열을 보관하고 있기 때문에 동일하게 char도 Unicode(UTF-16) 문자코드를 사용한다.


char a1 = '가';

System.out.printf("%02x",(int)a1);


결과: ac00


자바문자에서 임의의 문자코드로 변환

자바로 문자에서 문자 코드를 만들려면 String 클래스의 getBytes메서드를 사용한다.

String utfStr ="가나다";

System.out.print("UTF-8 :" );

byte[] utf8;

utf8 = utfStr.getBytes("UTF-8");

for(byte b1 : utf8) {

System.out.printf("%02x", b1);

}


System.out.println();


System.out.print("UTF-16 :" );

byte[] utf16;

utf16 = utfStr.getBytes("UTF-16");

for(byte b1 : utf16) {

System.out.printf("%02x", b1);

}


System.out.println();


System.out.print("UTF-32 :" );

byte[] utf32;

utf32 = utfStr.getBytes(Charset.forName("UTF-32"));

for(byte b1 : utf32) {

System.out.printf("%02x", b1);

}


System.out.println();


System.out.print("MS949 :" );

byte[] ms949;

ms949 = utfStr.getBytes(Charset.forName("MS949"));

for(byte b1 : ms949) {

System.out.printf("%02x", b1);

}


임의의 문자 코드로부터 자바 문자로 변환

byte[] utf16Java  = { (byte)0xC0, (byte)0xAC, (byte)0xB7, (byte)0x91};

System.out.println(new String(utf16Java, "UTF-16"));


byte[] ms949Java  = { (byte)0xBB, (byte)0xE7, (byte)0xB6, (byte)0xFB};

System.out.println(new String(ms949Java, "MS949"));


문자깨짐원인

실제 운영환경에서 문자깨짐발생

- 대부분의 경우 디폴트 인코딩 차이 때문이다.

개발환경에서는 윈도우를 이용하고 실제 운영환경에서는 리눅스를 사용하는 경우가 많다.

디폴트 인코딩을 사용하는 클래스나 메서드

String 생성자(인수없는것)

String 클래스의 getBytes 메서드(인수없는것)

FileReader 클래스

FileWriter 클래스

InputStreamReader 생성자(인수없는것)

OutputStreamWriter 생성자(인수없는것)


=>

FileReader 클래스와 FileWriter 클래스는 사용하지 않고, FileInputStream 클래스와 InputStreamReader 클래스로 대체한다.

그 외의 메서드나 생성자는 반드시 인코딩과 charset을 지정한다.


서로게이트 페어

자바가 내부에서는 사용하는 UTF-16은 서로게이트 페어(Surrogate Pair)라는 사양이 있다.

서로게이트 페어는 한마디로 말하면 '여러 문자(16비트 부호)로 한 문자를 표현하는 형식'으로, 자바의 세계에서는 '2개의 char로 하나의 문자를 표현하는 경우가 있다'라는 것을 의미한다.

일부 문자는 char 2개, 다른 문자는 char 1개와 같이 문자에 따라 필요한 char의 수가 다르다.

UTF-16에서 문자를 취급할 때, 문자수를 계산해서 제한하는 처리의 경우 서로게이트 페이에 대응할 필요가 생긴다.

대부분의 경우 서로게이트 페어를 금지하는 것은 간단하다.

Character클래스의 isLowSurrogate 메서드와 isHighSurrogate 메서드를 사용하면 그 문자가 서로게이이트 페어인지의여부를 확인할 수 있다.

그래서 서로게이트 페어라면 오류 처리를 실시하면 된다.



String str123 = "코알라⦁";

System.out.println(str123.length());

System.out.println(str123.codePointCount(0, str123.length()));


char[] chars = str123.toCharArray();

for (char ch : chars) {

if(Character.isLowSurrogate(ch) || Character.isHighSurrogate(ch)) {

System.out.println("서로게이트 페어가 포함되어 있는 문자열");

}

}


서로게이트 페어를 허용해야 하는 경우는 문자 수를 적절하게 처리해야한다.

대부분은 String 클래스의 length 메서드를 사용하여 문자를 계산하지만 서로게이트 페어레 대응하는 부분에서는 String 클래스의 codePointCount 메서드를 사용해야 한다.


intern 메서드로 같은 문자열찾기

String 클래스는 불변객체이다.

그럼 동일 문자열을 여러번 생성하면 그때마다 객체가 생성되서 메모리가 부족하게 되는 것이 아닌가?라고 생각할수 있다.

이러한 문제의 대책으로 String 클래스의 객체는 java VM이 관리하고 있다.

'aaa'와 같은 문자열을 작성하는 경우(문자리터럴) java VM에서 동일 객체를 참조한다는 구조가 포함되어 있다.

이것을 명시적으로 하는 것이 String 클래스의 intern 메서드다.



String zzz ="zzz";

String zz ="zz";

String z ="z";


System.out.println(zzz.equals(zz+z)); //true

System.out.println(zzz == (zz+z)); //false

System.out.println(zzz == (zz+z).intern()); //true


intern 메서드를 사용하여 java VM에서 동일한 문자열을 나타내는 객체를 취득하기 때문에 세번째 비교에서 true가 된다.

intern 메서드를 의식해서 이용하는 경우는 적을지도 모르지만 동일한 문자열이 여러번 등장하는 것을 알고 있는 경우는 이것을 이용함으로써 메모리를 절약할 수 있다.

서블릿에서 HttpSession 인터페이스의 getId 메서드로 취득한 것을 intern 메서드로 동일 객체를 검색하여 synchronized하는 것도 가능하다.

출처: 자바 마스터북

반응형

'JAVA > JAVA 기초' 카테고리의 다른 글

가시성  (0) 2018.10.25
값 전달 방법(callByValue callByReference)과 불변객체  (0) 2018.10.25
예외 관련  (0) 2018.10.21
자바 컬렉션관련  (0) 2018.10.13
자바 배열관련  (0) 2018.10.13