[C#] 34. 문자열 클래스, String과 StringBuilder를 사용하는 방법


Study/C#  2021. 9. 22. 16:41

안녕하세요. 명월입니다.


이 글은 C#에서 사용되는 문자열 클래스, String과 StringBuilder를 사용하는 방법에 대한 글입니다.


프로그램 상에서 사용하는 원시 자료형(데이터 타입)은 정수와 실수형이 많이 있지만, 문자열을 String 객체로 하나 있습니다.

정확히는 String 타입은 원시 자료형은 아니고 char의 배열 형식으로 이루어진 클래스 형식입니다.


즉, 문자의 하나는 ascii 코드로 되어 있는 정수형 타입이고 그 데이터 타입은 char입니다.

다시 정리하면 String은 char의 배열 형식으로 정의되어 있고, 프로그램 내에서는 String 타입으로 데이터를 넣으면 문자열로 출력이 됩니다.

using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 문자열 자료형
      string data = "Hello world";
      // string을 char[] 배열로 변환
      char[] binary = data.ToCharArray();
      // 각 배열의 값을
      foreach(var b in binary)
      {
        // 콘솔에 16진수로 출력
        // byte는 unsigned char
        Console.Write("0x{0:X2} ", (byte)b);
      }
      // 개행
      Console.WriteLine();
      // char의 배열을 생성
      // 0x48 0x65 0x6C 0x6C 0x6F
      // Hello
      char[] binary1 = new char[]
      {
        // byte 타입을 char로 변환, unsigned의 차이이므로 강제 캐스팅이 가능
        (char)0x48,(char)0x65,(char)0x6C,(char)0x6C,(char)0x6F
      };
      // char[]를 string으로 변환
      var data1 = new String(binary1);
      // 콘솔 출력
      Console.WriteLine(data1);
      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

위 예제를 보면 string 타입을 char[] 배열 타입으로 변환이 되고 char[] 배열 타입이 string 타입으로 변환하는 것을 확인할 수 있습니다.

링크 - 아스키 코드


여기까지 정리하면, String은 char[]의 배열 타입으로 되어 있고 그 정수 값은 아스키 코드로 이루어져 있다라고 설명했습니다.

여기서 다시 생각해야 할 것은 char의 데이터 크기는 1byte로 2^8으로 되어 있고 그 값은 0~255(unsigned char)로 되어 있습니다. 즉 크기가 256인데, 세상의 모든 문자를 256의 크기로 모두 나타낼 수 없습니다.

한글만 쳐도 256개를 넘게 있습니다만, 그래서 나온 문자열 인코딩 타입이 UTF-8이 있습니다.

사실 이 인코딩 타입에 대해서는 내용이 좀 복잡하고 설명할 부분이 많이 있지만, 지금은 표준 인코딩 문자열 타입이 UTF-8로 통일되어 있고 이제는 거의 대부분의 인코딩 타입이 UTF-8라고 생각하면 좋습니다.

이 UTF-8의 인코딩 타입은 가변 길이 문자로 최소 1바이트에서부터 4바이트까지 이용하여 문자열을 나타냅니다. 즉, 2^32승이 되므로 약 42억개의 문자를 나타낼 수 있습니다.

자세한 부분은 byte와 인코딩 부분에서 자세히 설명하겠습니다.


다시 String으로 돌아와서, C#에서 String의 데이터 구성은 char[] 배열입니다만 구조는 클래스의 형태로 구성되어 있습니다.

String 클래스에는 문자열을 다루기 위한 함수가 존재합니다.

IsNullOrEmpty, IsNullOrWhiteSpace

String은 클래스이기 때문에 null이 존재합니다. 그러므로 null의 값은 비교 연산자로 값의 여부를 확인할 수 있지만, null이 아닌 문자열이 없는 string도 존재합니다.

using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // string 문자열에 null를 선언
      string data = null;
      // data가 null이라면
      if (data == null)
      {
        // 콘솔 출력
        Console.WriteLine("Null");
      }
      // data에 null은 아니지만 문자열은 없다.
      data = "";
      // data가 null이라면
      if (data == null)
      {
        // 콘솔 출력
        Console.WriteLine("Null");
      }
      // null이거나 data의 값이 없으면 true
      if (string.IsNullOrEmpty(data))
      {
        // 콘솔 출력
        Console.WriteLine("IsNullOrEmpty");
      }
      // data에 space만 넣는다.
      data = "  ";
      // data에 null은 아니지만 문자열에 space만 있으면 true
      if (string.IsNullOrWhiteSpace(data))
      {
        // 콘솔 출력
        Console.WriteLine("IsNullOrWhiteSpace");
      }
      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

문자열 관계 함수 중에서는 가장 많이 사용하게 될 IsNullOrEmpty, IsNullOrWhiteSpace 함수입니다.

IsNullOrEmpty의 경우는 null 또는 문자열이 없는 경우에 true를, IsNullOrWhiteSpace의 경우는 공백의 경우까지 포함하면 true를 리턴하는 문자 확인 함수입니다.

Substring, Trim, Remove, Replace, Split

위 함수는 문자열을 자르거나 제거하는 함수입니다.

using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 문자열 데이터
      string data = "   Hello,world   ";
      // 문자열 데이터에서 5번째부터 5개를 추출
      Console.WriteLine("Substring = " + data.Substring(5, 5));
      // 문자열 앞뒤로 공백을 제거
      Console.WriteLine("Trim = " + data.Trim());
      // 문자열 5번째부터 5개를 공백으로 제거
      Console.WriteLine("Remove = " + data.Remove(5, 5));
      // 문자열에서 o를 a로 치환
      Console.WriteLine("Replace = " + data.Replace('o', 'a'));
      // 문자열을 ,를 기준으로 분할
      foreach(var d in data.Split(','))
      {
        // 콘솔 출력
        Console.WriteLine("Split = " + d);
      }
      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

Substring은 문자를 잘라서 가져오는 함수이고 Trim은 공백 제거, Remove는 문자 제거, Replace는 치환입니다.

Split은 문자열을 나누는 것입니다.

IndexOf, LastIndexOf

문자열의 위치를 가져오는 것입니다.

using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 문자열 데이터
      string data = "   Hello,world   ";
      // 문자열 앞에서부터 o의 문자가 있는 위치
      Console.WriteLine("IndexOf = " + data.IndexOf('o'));
      // 문자열 앞의 10번째부터 o의 문자가 있는 위치
      Console.WriteLine("IndexOf = " + data.IndexOf('o', 10));
      // 문자열 뒤에서부터 o의 문자가 있는 위치
      Console.WriteLine("LastIndexOf = " + data.LastIndexOf('o'));
      // 문자열 뒤의 10번째부터 o의 문자가 있는 위치
      Console.WriteLine("LastIndexOf = " + data.LastIndexOf('o', 10));
      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

문자열의 위치를 구하는 함수 입니다. 만약 문자열에 검색하고자 하는 문자열이 없으면 -1을 리턴합니다.

ToUpper, ToLower

ToUpper와 ToLower는 영어 문자열만 적용되는 함수입니다.

ToUpper은 문자열의 모든 문자를 대문자로, ToLower은 문자열의 모든 문자를 소문자로 변환하는 함수입니다.

using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 문자열 데이터
      string data = "hello WORLD";
      // 모든 문자를 대문자로 변환
      Console.WriteLine("ToUpper = " + data.ToUpper());
      // 모든 문자를 소문자로 변환
      Console.WriteLine("ToLower = " + data.ToLower());
      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

Compare, Equals

문자열 비교 함수입니다.

using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 문자열 데이터
      string data1 = "hello WORLD";
      string data2 = "hello world";
      // 문자열 비교 함수, 같으면 0, 다르면 -1
      Console.WriteLine("Compare = " + string.Compare(data1, data2));
      // 문자열 비교 함수 끝에, bool 값을 넣어 true 면 대소문자 구분없이 같은 문자, false면 대소문자 구분을 한다.
      // 결과는 대소문자 관계없이 같으면 0, 다르면 -1
      Console.WriteLine("Compare true = " + string.Compare(data1, data2, true));
      // 문자열 비교 함수, 같으면 true, 다르면 false
      Console.WriteLine("Equals = " + string.Equals(data1, data2));
      // 문자열 비교 함수 끝에 StringComparison.OrdinalIgnoreCase를 넣으면 대소문자 구분없이 비교한다.
      // 결과는 대소문자 관계없이 같으면 true, 다르면 false
      Console.WriteLine("Equals OrdinalIgnoreCase = " + string.Equals(data1, data2, StringComparison.OrdinalIgnoreCase));
      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

Concat, Join

문자열 함치기 함수입니다.

using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 문자열 데이터
      string data1 = "hello";
      string data2 = "world";
      // 문자열 합치기
      Console.WriteLine("Concat = " + string.Concat(data1, data2));
      // 문자열 합치기, (첫번째 파라미터는 합치는 문자열 사이의 구분자)
      Console.WriteLine("Concat = " + string.Join(",", data1, data2));
      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

Format

String 내에서 포멧팅을 할 수 있는 기능입니다.

포멧팅이란 int의 값을 string으로 변환하는 데, 소수점 2자리까지 표현 등의 포멧을 결정하는 것을 뜻합니다.

using System;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 문자 포멧팅
      Console.WriteLine("{0:yyyy/MM/dd}, {1:0.00}", DateTime.Now, 10.5555d);
      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}
문자타입 설명 사용법 예제
숫자 포멧팅
c 통화(돈 표시) {0:c} $ 55,674.74
e 지수 표시 {0:e} 5.567474e+004
f 고정 소수점 {0:f} 55674.74
g 표준 {0:g} 55674.73789621
n 1000단위 마다 콤마 찍기 {0:n} 55,674.74
포멧팅
0 0 자리 표시자 {0:00.00} 55674.74
# 숫자 자리 표시자 {0:(#).##} (55674).74
. 소수점 {0:0.000} 55674.738
, 천 단위 구분 기호 {0:0,0} 55,675
% 퍼센트 {0:0%} 5567474%

여기까지 자주 사용되는 String의 함수를 정리했습니다.

String.Format의 경우는 예전에 많이 사용하던 방식이고 최근에는 보간법으로 간단하게 사용합니다.

링크 - [C#] String 보간법(interpolation)


위 예제에서도 문자열을 합치는 데 Concat를 사용한다고 설명했지만 사실 연산 기호로도 문자열을 합칠 수 있습니다.

using System;

namespace Example
{
  class Program
  {
    static void Main(string[] args)
    {
      // 문자열 데이터
      string data1 = "hello";
      string data2 = "world";
      // 문자열 합치기
      Console.WriteLine(data1 + " " + data2);
      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

그런데 이런 방식의 문자열 합치기는 data1과 공백 하나가 있는 String이 합쳐지고 하나의 String 인스턴스가 생성됩니다.

다시 그 인스턴스에서 data2의 공백이 합쳐지고 또 하나의 인스턴스가 생성됩니다.

즉, Concat이나 연산자를 사용한 String에서의 문자열 합치기는 새로운 인스턴스를 생성하는 것입니다.

참고로 이 인스턴스를 생성하는 자원은 프로그램에서 상당히 느립니다.


그래서 인스턴스를 생성하지 않고 연속적으로 메모리에 붙일 수만 있다면 어떨까요? 인스턴스 생성하는 시간을 절약할 수 있기 때문에 상당히 빨라질 수 있습니다.

using System;
using System.Text;
using System.Diagnostics;

namespace Example
{
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 시간 측정 클래스
      Stopwatch sw = new Stopwatch();
      // 루프 변수
      const int loop_count = 100000;
      String test = null;
      // 측정 시작
      sw.Start();
      // 문자열 합치기
      for (int i = 0; i < loop_count; i++)
      {
        test += "Data 들어갑니다.\n";
      }
      // 측정 종료
      sw.Stop();
      // 콘솔 출력 - 처리 시간
      Console.WriteLine("Control time - " + sw.ElapsedMilliseconds);
      // 측정 리셋
      sw.Reset();
      //StringBuilder의 문자 붙이기
      StringBuilder test2 = new StringBuilder();
      // 측정 시작
      sw.Start();
      // 문자열 합치기
      for (int i = 0; i < loop_count; i++)
      {
        test2.Append("Data 들어갑니다.\n");
      }
      // 측정 종료
      sw.Stop();
      // 콘솔 출력 - 처리 시간
      Console.WriteLine("Control time - " + sw.ElapsedMilliseconds);
      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

결과를 보시면 StringBuilder에서 문자열을 합친 것이 압도적으로 빠르네요.

문자열의 데이터를 처리하는 데는 String 클래스의 함수를 써서 String 값을 계산하는 데 빠르지만, 문자열을 합치거나 삭제, 뺄 때는 StringBuilder를 사용하는 편이 훨씬 좋습니다.


여기까지 C#에서 사용되는 문자열 클래스, String과 StringBuilder를 사용하는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.