본문 바로가기
Delphi Tip/유닛

TStringList 삶아서 구워먹기

by MonoSoft 2021. 12. 31.
728x90
반응형

TStringList 삶아서 구워먹기

 

1. TStringList 란 무엇인가?

델파이 유닛중 Classes 라는 유닛에 기본적으로 포함되어 있는 클래스다.

 

 

TStringList = class(TStrings)

 

private

FList: PStringItemList;

FCount: Integer;

...

 

위와 같이 TStrings 를 상속받고 있다. TStrings 와 TStringList 는 다르다.

이름에서도 알 수 있듯이 이 클래스는 문자열(String)을 효율적으로 관리하기 위한

클래스다. 본인도 그러했으나 아마 대부분의 델파이 개발자들이 델파이를 처음

하면서 초기에 알게 되고, 그 어떤 클래스에 못지 않게 많이들 사용하는 클래스중에 

하나다. 델파이를 처음 배우기 시작한 개발자라면 알아두면 좋을만한 팁과 내용을 

아래에서 소개하고자 한다. (소스보면 다 알 수 있는거지만 초보님들을 위하여 ^^;;)

 

 

2. 어떻게 사용하는가?

다른 클래스와 다름없이 변수를 선언하고 Create 를 하고 사용을 한다. (너무 뻔한 내용 ㅋㅋ)

 

var

 

S: TStringList; // 이하 모든 TStringList 는 S 로 표기한다. Create 문이 없더라도 했음을 가정한다.

begin

S := TStringList.Create;

....

 

클래스명만 봐도 이넘이 StringList 니까 String 라는 것을

Item 으로 가지고 있다는 것을 알 수 있다.

그러나 '문자(string)' 만을 목록으로 가지는게 아니다.

 

이 리스트에 string 을 하나 추가해보자.

 

S.Add('망초');

 

내부 소스를 들여다 보면,

 

function TStringList.Add(const S: string): Integer;

begin

Result := AddObject(S, nil);

end;

 

즉, S.Add('망초'); 하는것과

S.AddObject('망초',nil); 하는것과 같은 코드라는 말이 된다.

 

이 코드를 기억하자.

TStringList 에서 제공하는 Add 는 AddObject 이다.

TStringList 는 그냥 String 만 목록으로 가지고 있는게 아니고

해당 String 에 Match 되는 Object 를 함께 가질 수 있다.

 

[ 팁1 : 이름만 TStringList 지 실제로 ObjectList 역할도 함께 한다. ]

 

AddObject 니까 무조건 TObject 타입만 넣을 수 있는가?

 

아래와 같이 해 보길 바란다.

 

S.AddObject('망초',TObject(25));

ShowMessage(IntToStr(Integer(S.Objects[0])));

이상없이 잘 컴파일 되는것을 확인할 수 있을 것이다.

 

[ 팁2 : Integer 도 Object 에 넣을 수 있다. ]

 

문자도 object 에 넣을 수 있는가? 있다.

 

S.AddObject('도련',TObject('미남')); // 에러가 난다.

var

Temp: String;

begin

Temp := '미남';

S.AddObject('도련',TObject(A));

ShowMessage(String(S.Objects[0]));

 


역시 된다.

 

[ 팁3 : String 도 Object 에 넣을 수 있다. ]

 

이번에는 또 다른것을 Object 에 한번 넣어보자.

 

type

 

// 친구 정보를 담고 있는 레코드

PFriendInfo = ^TFriendInfo

TFriendInfo = record

Name: String;

Age: Integer;

Tel: String;

end;

 

...

 

var

FriendInfo := New(PFriendInfo);

FriendInfo.Name := '낭자';

FriendInfo.Age := 18;

FriendInfo.Tel := '01x-xxx-xxxx';

S.AddObject('망초',TObject(FriendInfo));

 

역시 이것도 된다. 

 

[ 팁4 : Pointer 도 TObject 로 Type Cast 를 통하여 Object 목록에 담을 수 있다. ]

 

위의 예제들로 TStringList 는 그냥 이름대로 문자만 담는 목록이 아니라

'여러가지' 를 담을 수 있는 목록이라는것을 알 수 있다.

꼭 기억하기 바란다. ^^;

 

3. Index 가 아니라 Name 으로 다루기

일반적으로 리스트에 아이템을 가져오기 위해 Index 를 사용한다.

Value := S.Strings[0]; // 첫번째 아이템을 가져와서 Value 라는 변수에 담으라는 뜻이다.

 

그럼 이렇게 해보자.

S.Values['Name'] := '망초';

ShowMessage(S.Values['Name']);

된다. ( 이미 다른분이 강좌란을 통해 이미 글을 쓰신적이 있는 내용이다. )

 

일반적으로 Add('망초') 이런식으로 쓰지 않아도 위 코드처럼 이름으로 쓸 수도 있다.

참으로 편리한 기능이다. (필자생각^^)

 

위에서 사용된적이 있는 TFriendInfo 라는 record 대신 TStringList 를 써보자.

 

S.Values['Name'] := '망초';

S.Values['Age'] := '25';

S.VAlues['Tel'] := '01x-xxx-xxxx-';

 

이제 S 라는 TStringList 는 '망초' 이라는 사람의 정보를 담고 있는 Object 가 된것이다.

 

[ 팁5 : Index 대신 Name 으로 아이템을 다룰 수 있다. ]

 

4. 여러문장을 한문장으로 또는 한문장을 여러문장으로 만들기

 

TStringList 에는 여러개의 문자들이 들어간다.

이 문자들을 한 문자열로 바꾸어 보자.

 

S.Add('01x');

S.Add('123');

S.Add('4567');

 

일반적으로 1 개의 String 으로 만들기 위하여 S.Text 를 사용해 왔었다. 

그렇게 하면 문자 사이에 개행문자(#13#10) 이 포함된 문장으로 나오게 된다.

전화번호 형식처럼 - 를 구분자로 해서 한문장으로 뽑아보자.

 

S.Delimiter := '-';

ShowMessage(S.DelimitedText);

어떤가? 01x-123-4567 이라고 나오지 않았는가?

 

[ 팁6 : TStringList 는 Delimiter 를 통하여 구분자를 변경할 수 있다. ]

 

이번에는 반대로 01x-123-4567 이라는 1 개의 문장을 여러개의 문장으로 변경해보자.

 

S.Delimiter := '-';

S.DelimitedText := '01x-123-4567';

ShowMessage(S.Strings[0]);

 

01x 만 나오지 않는가? 

CSV 파일형식처럼 단어마다 콤마(,)를 찍어주는 CommaText 라는것도 있는데

소스를 보면 마찬가지로 Delimiter 를 사용한다.

 

[ 팁7 : TStringList 는 Delimiter 로 String Spliter 의 기능도 한다. ]

 

5. 중복 데이터를 막자

​중복을 막기 위해 기존에 여러분들은 어떻게 하였는가? 혹시 아래와 같이 하진 않았는가?

 

n := S.IndexOf('문자');

if n<0 then S.Add('도련');

 

TStringList 에는 Sorted 라는 property 가 있다. 다음과 같이 해 보자.

S.Sorted := True;

S.Add('망초');

S.Add('망초');

S.Add('망초');

ShowMessage(S.Text);

 

어떤가? '망초' 이라는 문자는 중복되어 Add 되지 않음을 알 수 있다.

Sorted 라는 속성은 중복방지 뿐만 아니라 아래에서 다른 여러가지 기능도 가지고 있다.

 

[ 팁8 : Sorted 속성을 이용하면 중복데이터를 방지할 수 있다 ]

 

5. 데이타를 정렬해보자

위에서 설명한 Sorted 속성을 다시한번 보기로 하자. 속성명에서도 알 수 있듯이 

이 속성은 정렬을 할 수 있게 해 준다. TStringList 가 가지고 있는 procedure Sort; 와는

개념이 다르다.

 

정렬을 위해 여러분은 어떻게 하였는가?

 

S.Add('3');

S.Add('1');

S.Add('2');

S.Sort;

 

위와 같지 하지는 않았는가? 중복데이터를 허용해야 하며 정렬이 필요할 경우엔 위 처럼

데이타의 Add 가 끝난후 한번의 Sort procedure 를 호출함으로써 정렬할 수 있다.

 

만약, 데이타가 중복되지 않는다면 위에서 나왔던 Sorted 속성을 True 로 해줌으로써

모든 정렬은 TStringList 가 알아서 한다.

 

S.Sorted := True;

S.Add('3');

S.Add('1');

S.Add('2');

 

Add 하는 순간에 자동으로 정렬되어 지는 것이다.

순서를 바꿔 Sorted 를 Add 다음에 쓰면 어떻게 될까?

 

S.Add('3');

S.Add('1');

S.Add('2');

S.Add('2');

S.Sorted := True;

 

2 라는 문자가 2 번 중복되었다.

그러나 Add 이후에 Sorted 속성을 바꿈은 

기존 중복데이터를 제거하지는 않는다. 정렬만 된다. ^^;

 

[ 팁9 : Sorted 속성으로도 쉽게 정렬할 수 있다 ]

 

 

6. 알고보면 빠른 IndexOf

TStringList 에 Add 한 문자를 찾기 위해 일반적으로 IndexOf 를 많이 사용할 것이다.

IndexOf 의 소스코드를 본적이 있는가? 

 

function TStringList.IndexOf(const S: string): Integer;

begin

if not Sorted then Result := inherited IndexOf(S) else

if not Find(S, Result) then Result := -1;

end;

 

IndexOf 에서도 Sorted 라는 속성이 사용되는것을 볼 수 있다.

여러분들은 TStringList 가 이진검색을 지원한다는것을 알고 있었는가?

 

만약 Sorted 속성이 False 라면.. TStrings 의 IndexOf 를 사용하게 되는데 

이 소스를 보면..

 

function TStrings.IndexOf(const S: string): Integer;

begin

for Result := 0 to GetCount - 1 do

if CompareStrings(Get(Result), S) = 0 then Exit;

Result := -1;

end;

 

그냥 단순 loop 다. 데이타가 많아지면 많아질수록 얼마나 느려질것인가;; -.-;

만약 Sorted 속성이 True 일 경우엔 Find() 를 이용한다. 소스를 보자.

 

function TStringList.Find(const S: string; var Index: Integer): Boolean;

var

L, H, I, C: Integer;

begin

Result := False;

L := 0;

H := FCount - 1;

while L <= H do

begin

I := (L + H) shr 1;

C := CompareStrings(FList^[I].FString, S);

if C < 0 then L := I + 1 else

begin

H := I - 1;

if C = 0 then

begin

Result := True;

 

if Duplicates <> dupAccept then L := I;

end;

end;

end;

Index := L;

end;

 

느낌이 팍! 올것이다. 바로 이진검색인것이다.

이진검색에 대한 내용은 본 강좌의 주제가 아니므로

더 알고싶은분은 알아서 찾아보길 바란다. ^^;; (죄송;)

 

[ 팁10 : TStringList 는 Sorted 가 True 일때 이진검색을 한다 ]

 

728x90
반응형

댓글