본문 바로가기
프로그래밍 언어/JAVA

Java String(SubString, StringBuilder, Tokenizing, Split 등)

by kgvovc 2021. 5. 1.
반응형

String

 

String, StringBuilder의 차이

 

String은 immutable하고, StringBuilder는 mutable하다.

따라서 String 연산을 할 때 차이가 생기는데, 아래의 예제를 보자.

 

 

String불변 객체이기 때문에 concatenation을 하면 새로운 String 객체를 생성한다.

 

 

 

 

그러나, StringBuffer변할 수 있는 mutable 객체이기 때문에

객체를 직접 조작 가능하다.

 

 

 

Substring 만들기

substring() 메서드는 해당 문자열을 직접 변형하는 것이 아니라

새로운 String 인스턴스를 생성하는 것임에 주의...

위에서도 알 수 있듯이 Stringimmutable하다.

 

그리고, index는 zero-based(0부터 시작)임에 주의한다.

 

 

Method Signature

  • 문자열.substring(start)

    • start index부터 맨 끝까지 추출
  • 문자열.substring(start, last)

    • start index부터 last index 전까지 추출
    • [start, last)를 추출하는 거임

 

public class SubStringDemo {
    public static void main(String[] args) {
        String a = "Java is great.";
        System.out.println(a);
        String b = a.substring(5);
        System.out.println(b);
        String c = a.substring(5, 7);
        System.out.println(c);
        String d = a.substring(5, a.length());
        System.out.println(d);
    }
}

/* output */
Java is great.
is great.
is
is great.

 

 

 

String Tokenizing하기 (split)

 

(1번) 정규 표현식과 split() 메서드 이용

public class Tokenizing_Split {
    public static void main(String[] args) {
        String str = "abc dfs asdf emrwe sd";
        for (String word : str.split(" ")) {
            System.out.println(word);
        }
    }
}

(나중에 정규 표현식을 공부할 때 더 자세히 포스팅하겠다...)

 

 

 

(2번) StringTokenizer 클래스의 hasMoreTokens()nextToken() 메서드 이용

 

  1. StringTokenizer 클래스를 Tokenizing 하고 싶은 String으로 초기화
  2. hasMoreTokens()nextToken() 메서드로 tokenizing된 String들 추출

 

 

 

단어 기준(공백 기준) 분리

 

Method Signature

new StringTokenizer(String strToTokenize)

 

public class Tokenizing_Tokenizer {
    public static void main(String[] args) {
        StringTokenizer st = new StringTokenizer("Hello World Of Java");

        while (st.hasMoreTokens()) {
            System.out.println("Token : " + st.nextToken());
        }
    }
}
/* output */
Token : Hello
Token : World
Token : Of
Token : Java

 

  • 생성자에 String만 넘겨주면 European language들에서 "word boundary"로 생각되는 기준으로 잘린다.

    • 무슨 말이냐면 일반적으로 생각할 수 있는 "단어" 기준으로 잘린다는 말이다.

    • StringTokenizer st = new StringTokenizer("Hello \tWorld    Of\n Java");
      

      위와 같이 넘겨줘도 위의 예제와 똑같이 나온다.

 

 

지정한 문자 기준 분리

 

Method Signature

new StringTokenizer(String strToTokenize, String delimiters)
// 두번째 파라미터인 delimiters에 tokenizing 기준 문자들을 나열한 string을 넘겨주자

 

StringTokenizer st2 = new StringTokenizer("Hello, World|of|Java", ", |"); 
// 위에서 기준 문자는 총 세 개(',', ' ', '|')다. -> 공백도 포함임

while (st2.hasMoreElements()) {
    System.out.println("Token : " + st2.nextElement());
}

/* output */
Token : Hello
Token : World
Token : of
Token : Java

 

 

 

 

여기서 주의할 점은

delimiter가 연속적으로 위치하면,

그 사이 값이 null로 나오지 않고 그냥 함께 버려진다는 것이다.

무슨 말이냐면,

위의 예에서

Hello null World of Java로 나오지 않고

Helo World of Java로 나온다는 뜻이다.

 

 

이런 예시를 들어보자.

FirstName|LastName|Company|PhoneNumber로 인적사항이 표시되어 있는 String이 있는데

우리는 위의 String을 tokenizing 해서 각각의 정보를 얻고 싶다.

그런데 여기서 무직인 사람은 세번째 Company에 입력이 안되어 있다고 하자.

 

그렇다면 FirstName|LastName||PhoneNumber와 같은 형식으로 String이 작성되고,

이것을 StringTokenizer에 넣어서 '|' 기준으로 tokenizing 하면,

위에서처럼 delimiter가 연속으로 붙어있기 때문에 그냥 같이 버려지고

FirstName, LastName, PhoneNumber 세 개의 값만 나오게 된다.

이러한 문제점은 아래에서 해결해보도록 하자.

문제점을 해결하기 전에 알아야 할 Method Signature가 있으니 이것부터 공부해보자.

 

 

Method Signature

new StringTokenizer(String strToTokenize, String delims, boolean returnDelims);
// 세번째 파라미터 returnDelims가 true이면 delim도 같이 리턴한다.

 

StringTokenizer st = new StringTokenizer("Hello, World|of|Java", ", |", true);
while(st.hasMoreElements()){
	System.out.println("Token: " + st.nextElement());
}
/* output */
Token: Hello
Token: ,
Token:  
Token: World
Token: |
Token: of
Token: |
Token: Java

 

세번째 인자를 true로 넘겨주면 아래의 그림과 같이 동작한다.

 

 

 

 

SOLUTION

public class StrTok {
    public final static int MAXFIELDS = 4;
    public final static String DELIM = "|";


    public static String[] process(String line) {
        String[] results = new String[MAXFIELDS];

        StringTokenizer st = new StringTokenizer(line, DELIM, true);

        int i = 0;
        while(st.hasMoreTokens()) {
            String s = st.nextToken();
            if (s.equals(DELIM)) {
                if(++i >= MAXFIELDS)
                    throw new IllegalArgumentException("Input line " + line + " has too many fields");

                continue;
            }
            results[i] = s;
        }
        return results;
    }

    public static void printResults(String input) {
        System.out.println("Input: " + input);
        String[] outputs = process(input);

        int i = 0;
        for (String s : outputs) {
            System.out.println("Output " + i++ + " was: " + s);
        }
        System.out.println();
    }

    public static void main(String[] args) {
        printResults("A|B|C|D");
        printResults("|B|C|");
        printResults("A||C|D");
        printResults("|||");
        printResults("FirstName|LastName||PhoneNumber");
    }
}


/* output */
Input: A|B|C|D
Output 0 was: A
Output 1 was: B
Output 2 was: C
Output 3 was: D

Input: |B|C|
Output 0 was: null
Output 1 was: B
Output 2 was: C
Output 3 was: null

Input: A||C|D
Output 0 was: A
Output 1 was: null
Output 2 was: C
Output 3 was: D

Input: |||
Output 0 was: null
Output 1 was: null
Output 2 was: null
Output 3 was: null

Input: FirstName|LastName||PhoneNumber
Output 0 was: FirstName
Output 1 was: LastName
Output 2 was: null
Output 3 was: PhoneNumber

 

 

 

StringBuilder로 String 이어 붙이기

String을 concatenation 하는 가장 쉬운 방법은 "+" operator를 이용하는 것이다.

컴파일러는 암묵적으로 StringBuilder를 만들어서 그 안의 append() 메서드를 이용할 수 있도록 최적화를 해주기도 한다.

 

참고: String 최적화 JDK 1.5

https://gist.github.com/benelog/b81b4434fb8f2220cd0e900be1634753

 

컴파일러가 이 일을 처리하게 냅두지 말고 우리가 직접 해보자.

 

 

StringBuilder는 기본적으로 character들의 collection을 나타낸다.

StringBuilderString과 비슷하지만, 맨 위의 그림에서 나타나듯이,

Stringimmutable(불변)하지만,

StringBuildermutable하다.

 

 

참고로,

StringBuilderStringBuffer의 차이점은

StringBuilder는 synchronization(동기화)이 적용되지 않아 thread 환경에서 위험하고,

StringBuffer는 synchronization(동기화)이 적용되어 thread 환경에서 안전하다.

 

공통점은 두 클래스 모두 변경가능(mutable)하다.

두 클래스는 모두 AbstractStringBuilder라는 같은 부모 클래스의 자식 클래스고,

대부분의 메서드가 비슷하다.

 

 

아래의 예제를 보자.

public class StringBuilderDemo {

    public static void main(String[] args) {
        String s1 = "Hello" + ", " + "World";
        System.out.println(s1);

        StringBuilder sb2 = new StringBuilder();
        sb2.append("Hello");
        sb2.append(',');
        sb2.append(' ');
        sb2.append("World");

        String s2 = sb2.toString();
        System.out.println(s2);


        // Now do the above all over again,
        // but in a more concise (and typical "real-world" Java) fashion
        System.out.println(
                new StringBuilder()
                        .append("Hello")
                        .append(',')
                        .append(' ')
                        .append("World")
        );
    }
}

 

  • StringBuilder의 append 메서드는 StringBuilder 자신을 리턴하기 때문에

    연쇄적으로 메서드를 호출할 수 있다.

    • append(), delete(), deleteCharAt(), insert(), replace, reverse() 등의 메서드 모두 자기 자신을 리턴한다.

 

 

 

두 번째 예제는 StringBuilder로 String을 CSV 형태로 만드는 예제다.

public class StringBuilderCommaList {

    public static void main(String[] args) {
        // Method using regexp split
        StringBuilder sb1 = new StringBuilder();
        for (String word : "say hello to my lil friends".split(" ")) {
            if (sb1.length() > 0) {
                sb1.append(", ");
            }
            sb1.append(word);
        }
        System.out.println(sb1);

        // Method using a StringTokenizer
        StringTokenizer st = new StringTokenizer("say hello to my lil friends");
        StringBuilder sb2 = new StringBuilder();

        while(true) {
            sb2.append(st.nextToken());
            if (st.hasMoreTokens()) {
                sb2.append(", ");
            } else {
                break;
            }
        }
        System.out.println(sb2);
    }
}

 

 

 

String 내부의 Character 접근

 

String을 글자 단위로 접근하려면 charAt() 메서드를 사용한다.

public class StrCharAt {
    public static void main(String[] args) {
        String str = "hello!";
        for (int i = 0; i < str.length(); i++) {
            System.out.printf("Char %d is %c\n", i, str.charAt(i));
        }
    }
}
/* output */
Char 0 is h
Char 1 is e
Char 2 is l
Char 3 is l
Char 4 is o
Char 5 is !

 

 

String str2 = "hello world!";
for (char c : str2) {
    System.out.println(c);
}

위와 같이 Enhanced For Loop을 사용할 때,

(char ch: String)꼴을 사용하면 돌아갈 것 같지만, 안된다.

 

대신에 아래와 같이 String.toCharArray()를 사용해야 한다.

String str2 = "hello world!";
for (char c : str2.toCharArray()) {
    System.out.println(c);
}
반응형

댓글