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 일때 이진검색을 한다 ]
'Delphi Tip > 유닛' 카테고리의 다른 글
루프 시간측정 성능테스트 (0) | 2022.01.05 |
---|---|
TStringList 의 StrictDelimiter 프로퍼티 (0) | 2022.01.04 |
프로그램 중복 실행 방지 (0) | 2021.12.30 |
DateUtils.pas 날짜 연산 정리 (0) | 2021.12.29 |
스트링리스트 중복문자 갯수알아내기 (0) | 2021.12.28 |
댓글