볼랜드 F1 헬프 번역 프로젝트의 일환.
String Types 페이지 번역본임.
  
 
 
 
 
String Types
 
3가지 타입이 있음.
  • Short strings.
  • Long strings.
  • Wide (Unicode) strings. 
 
 Type Maximum Length  Memory required  Used for 
 ShortString  255 characters  2 to 256 bytes  이전 버전과의 호환을 위해 존재함.
 AnsiString  ~ 2^31 characters   4 bytes to 2GB  8-bit (ANSI) characters, DBCS ANSI, MBCS ANSI, etc.
 WideString  ~ 2^30 characters  4 bytes to 2GB   유니코드. 다중사용자 서버나 다중언어 지원용 어플리케이션에서 사용.
 
(Win32 플랫폼을 기준으로)
AnsiString: 때때로 long string 이라 불리며, 많은 목적으로 대부분 이걸 사용함.
WideString: .Net 플랫폼에서 주로 사용함.
 
String 타입은 컴파일러가 필요한 컨버전을 자동으로 해준다.
하지만 함수에서 레퍼런스 타입의 매개변수로 사용할 경우에는 반드시 정확한 타입을 맞춰주어야 한다.
String 타입은 명시적 형변환이 가능하다.
 
 예를 들어,
var S: string;
위와 같은 코드는 string 을 담을 변수 S를 생성한다.
Win32 플랫폼에서 컴파일러는 string 을 AnsiString 으로 해석한다. (만약, var S: String[3] 하는 식의 배열로 선언되어 있지 않을 경우)
.NET 플랫폼에서는 string 타입을 System.String 클래스로 맵핑한다.
물론 .NET 플랫폼에서도 1바이트 character 로 구성되는 String 을 사용할 수 있지만, 그러기 위해선 반드시 AnsiString 이라고 명시적 선언을 정확히 해줘야 한다.
Win32 플랫폼에서 {$H-} 라는 지시자를 통해 ShortString 으로 전환이 가능하다.
하지만 {$H-} 지시자를 .NET 플랫폼에서 사용하는 것은 권장하지 않는다.
기본제공함수인 Length() 는 string 이 포함하는 characters 의 숫자를 리턴한다.
SetLength() 함수는 string 의 길이를 정할 수 있게 해준다.
String 타입의 비교는 string 내의 characters 의 위치순서로 정의된다.
길이가 서로 다른 strings 사이에서, 짧은 스트링의 위치와 상응하지 않는 긴 스트링의 각 characters 는 < (greater-than) 연산에 포함된다.
예를 들어, 'AB' 는 'A' 보다 크다. 'AB' > 'A' 는 True 를 반환한다.
길이가 0 인 string 은 가장 작은 값을 가진다.
string 변수는 마치 배열을 사용하듯이 인덱싱이 가능하다.
만약 S가 string 이고 i 가 Integer 일 경우, S[i] 는 S 라는 string 에서 i 번째에 있는 character 를 가르킨다. 엄밀히 말하자면, S 배열에 있는 i 번째 바이트다.
ShortString 이나 AnsiString 의 경우, S[i] 는 AnsiChar 이다.
WideString 의 경우 S[i] 는 WideChar 이다.
single-byte (Western) 로케일에서, MyString[2]:='A' 라는 문장은 MyString 의 두번째 character 를 'A' 정하는 것이다. (엄밀히 말해, MyString 의 두번째 배열에 65 라는 바이트를 세팅)
아래의 코드는 일반적인 AnsiUpperCase 함수를 이용해 MyString 을 대문자로 전환하는 코드이다.
var I: Integer;
begin
   I := Length(MyString);
   while I > 0 do
    begin
       MyString[I] := AnsiUpperCase(MyString[I]);
       I := I - 1;
    end;
end;
이런 방법으로 string 타입을 인덱싱할 때는 string 의 마지막에 overwriting 을 할 때 access violations 에 주의해야 한다.
또한, 긴 string 을 레퍼런스 타입으로 넘기는 것(=var s: string)도 비효율적이므로 피해야 한다.
string 타입도 const 로 선언하는 것이 가능하며, 함수의 리턴값으로 string 을 설정하는 것 등도 가능하다.
string 의 길이는 동적으로 가변적이다.
예를 들어 아래와 같은 코드가 모두 가능하다.
MyString := 'Hello world!';
MyString := 'Hello' + 'world';
MyString := MyString + '!';
MyString := ' '; { space }
MyString := '';  { empty string }

 

 

 

 

 

 

Short Strings

ShortString 은 0 부터 255자(字=character) 까지의 길이로 이루어진다.

ShortString 의 길이는 동적으로 가변적이며, 메모리 공간에 정적으로 256 Bytes 를 구성한다.

  • 첫번째 바이트는 string 의 길이를 저장하고 있으며, 남은 255 바이트는 가능한 char 들이 들어간다.

만약 S 가 ShortString 일 경우, Ord(S[0]), Length(S) 는 S의 길이를 리턴한다.

S[0] 에 어떤 값을 대입하면 마치 SetLength 를 호출한 것과 같이 S 의 길이를 변경시킨다.

ShortString 은 하위호환성을 위해서만 존재한다.

델파이는 최대길이가 0 부터 255자인 short-string 타입을 지원한다. (사실상 ShortString 의 서브타입)

이것들은 예약어인 string 의 뒤에 [ ] 로 묶여 표시된다.

 

예를 들어,

var MyString: string[100];

위와 같은 코드는 100자의 길이를 가진 MyString 이라는 변수를 생성한다.

이것은 아래의 코드와 동일하다.

type CString = string[100];
var MyString: CString;

이런 방법으로 선언된 변수들은 타입이 요구하는 이상의 메모리를 할당한다. 그것은 최대길이 더하기 1바이트이다.

우리의 예에서, MyString 은 101 바이트이며, 이것은 이미 256 bytes 라고 선언되어 있는 변수인 ShortString 과 비교된다.

short-string 변수를 사용하는데 만약, 그 문자열이 최대길이를 넘어간다면 최대길이 이후부터 자동으로 잘린다.

일반함수인 High() 와 Low() 는 short-string 타입을 다룰 수 있다.

High()는 short-string 타입의 최대길이를 리턴하고, Low 는 0 을 리턴한다.

 
 
 
 
 
 
 

Long Strings

long string 이라고도 부르는 AnsiString은 동적으로 할당되는 문자열이다. 최대길이는 메모리가 허락하는 한도까지이다.
 
long string 변수는 4 바이트의 메모리 공간을 차지하는 포인터다.
문자열이 비어있을 때 (=길이가 0 일 때), 그 포인터는 nil 을 가리키며, 추가적으로 사용하는 공간은 없다.
문자열이 비어 있지 않을 때, 그 포인터는 문자열이 들어있는 동적할당된 메모리의 블럭을 가리킨다.
문자열 위치 이전의 8 바이트는 32-bit 길이의 지시자와 32-bit 길이의 레퍼런스 카운트로 이루어져 있다.
이 메모리는 힙 영역에 할당되지만, 이 메모리는 프로그래머가 관리하지 않고 전부 자동적으로 관리된다.
 
왜냐하면 long-string 변수들은 포인터이며, 둘 이상의 이 변수들은 추가적인 메모리의 소비없이 같은 값을 가리킬 수도 있기 때문이다.
컴파일러는 이 메모리를 이용해 자원을 절약하고, 실행속도를 향상시킨다.
long-string 변수가 해제되거나 생성될 때, 레퍼런스 카운트는 감소되거나 증가한다. string 을 가리키고 있던 레퍼런스 카운트가 0 이 되면 그 string 의 메모리는 해제된다.
이 프로세스를 레퍼런스 카운팅이라고 부른다.
인덱싱 과정에서 string 의 문자 하나를 바꿀 경우, 만약 그 레퍼런스 카운트가 1 보다 크다면 그 string 의 복사본이 생성된다.
이것을 가리켜 copy-on-write 라 한다.
 
 
 
 
 
 
 
 
 
 

 WideString

WideString 타입은 동적으로 할당되는 16-bit 유니코드 문자로 이루어진 문자열을 의미한다.
가장 관심을 가지고 봐야할 점은 WideString 과 AnsiString 간의 유사성이다.
Win32 에서, WideString 은 COM BSTR 타입과 호환된다.
 

Note: Win32 에서, WideString 은 레퍼런스 카운트를 이용하지 않는다.

 

Win32 플랫폼은 싱글바이트, 멀티바이트뿐만 아니라 유니코드를 지원한다.

SBCS(single-byte character set) 에서 각 바이트는 문자열의 한 character 와 일치한다. 
 
MBCS(multibyte character set)에서는 일부 character 는 1바이트 또는 그 이상의 바이트를 통해 표현된다.
MBCS에서 가장 첫번째 바이트는 lead byte 라고 불린다.
일반적으로, MBCS에서 하위 128 characters 들은 7-bit ASCII characters 이며, 127 이상의 character 를 결정하는 건 MBCS의 lead byte 이다.
null 값은(#0) 언제나 싱글바이트 문자다.
MBCS (특히 DBCS[double-byte character sets]) 는 아시아 언어권에서 널리 사용된다.
 
유니코드 character set 에서 각 문자는 2바이트를 의미한다.
그러므로 유니코드 문자열은 1바이트의 나열이 아니라 2바이트의 나열이다.
Unicode characters 와 strings 는 또한 wide characters 와 wide character strings 로 불린다.
Unicode characters map 의 처음 245은 ANSI character set 이다. 
윈도우즈 OS는 유니코드를 지원한다. (UCS-2) 
 
델파이는 Char, PChar, AnsiChar, PAnsiChar, AnsiString 타입을 통해 싱글바이트와 멀티바이트 문자(열)을 지원한다.
S[i]가 i번째 바이트라는 식의 계산으로는, 멀티바이트 문자열의 인덱싱은 신뢰할 수 없다. 
그러나, 표준 String 은 또한 locale-specific 한 문자열 관리가 가능하다.
(멀티바이트 함수들은 보통 Ansi- 로 시작하는 이름을 가진다. 예를 들어, StrPos 의 멀티바이트 버전 함수는 AnsiStrPos 임)
멀티바이트 문자의 지원은 로케일 별 OS 에 의존적이다.
델파이는 WideChar, PWideChar, WideString 을 통해 유니코드를 지원한다.
 
 
 
 
 
 

Working with null-Terminated Strings

C/C++ 를 포함한 많은 프로그래밍 언어들이 문자열 전용의 데이터 타입이 없다.
이들 프로그래밍 언어들과 이 언어들로 빌드된 프로그램들은, null-terminated 문자열에 의존하고 있다.
null-terminated 문자열이란 null 문자(#0)로 종료되는 0 기반의 문자배열이다.  
배열의 길이를 가리키는 부분이 없기 때문에, 처음 만나는 널문자가 문자열의 끝을 나타낸다. 
델파이를 사용하는 프로그래머는 null-terminated 문자열을 사용하는 시스템과 데이터를 공유하기 위해 SysUtils 유닛에 있는 특수한 루틴들과 구조들을 사용할 수 있다. 
 
예를 들어, 아래의 코드는 null-terminated 문자열을 저장하기 위한 선언이다.
 
type
  TIdentifier = array[0..15] of Char;
  TFileName = array[0..259] of Char;
  TMemoText = array[0..1023] of WideChar;
 
또한 확장표현({$X+})을 사용하여 0 기반 문자배열을 정적으로 할당할 수 있다.
(동적배열은 이런 목적으로 동작하지는 않을 것이다.)
만약 문자열 배열상수를 초기화할 때, 선언된 길이보다 짧은 문자열로 초기화했다면, 남은 문자들은 모두 #0 으로 초기화 될 것이다.
 
 
 
Using Pointers, Arrays, and String Constants
null-terminated 문자열을 잘 다루기 위해, 때때로 포인터를 사용해야 할 경우가 있다.
문자열 상수는 Char, WideChar 로 이루어진 null-terminated 배열을 가리키는 포인터인 PChar, PWideChar 와 호환이 된다.
 
 
var P: PChar;
  ...      
P := 'Hello world!'
 
위의 코드에서 P는 'Hello world!' 라는 문자를 복사해서 저장하고 있는 null-terminated 메모리 구역을 가리킨다.
 
이 코드는 아래와 동일한 의미를 지닌다.
const TempString: array[0..12] of Char = 'Hello world!';
var P: PChar;
   ...
P := @TempString[0];
 
물론 문자열 상수를 어떠한 함수의 값이나 const PChar, const PWideChar 형태의 매개변수로써 전달하는 것도 가능하다. - 예를 들어 StrUpper('Hello world!') 같은 형태로.
PChar 를 할당할 경우 컴파일러는 null-terminated 문자열의 복사본을 만들고 함수에는 그 복사본의 포인터를 전달한다.
마지막으로, PChar, PWideChar 의 상수를 문자만으로 이루어진 문자열이나, 구조체 타입으로 초기화가 가능하다.
 
예를 들어,
const
  Message: PChar = 'Program terminated';
  Prompt: PChar = 'Enter values: ';
  Digits: array[0..9] of PChar = ('Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine');
 
0 기반 문자배열은 PChar, PWideChar 와 호환된다.
만약 문자배열의 포인터를 사용한다면, 컴파일러는 배열을 배열의 첫번째 요소의 주소값을 가지는 포인터상수로 변환한다.
 
예를 들어,
var
  MyArray: array[0..32] of Char;
  MyPointer: PChar;
begin
  MyArray := 'Hello';
  MyPointer := MyArray;
  SomeProcedure(MyArray);
  SomeProcedure(MyPointer);
end;
 
위와 같은 코드는 같은 값을 매개변수로 SomeProcedure 라는 함수를 두번 호출하는 것과 같다.
 
만약 문자 포인터가 배열일 경우 그 포인터는 인덱싱이 가능하다.
이전의 예에서 MyPointer[0] 은 H 를 리턴한다.
인덱싱은 데이터를 꺼내오기 전, 포인터에서 인덱싱 된 수만큼 더해진 오프셋을 가리킨다. (PWideChar 의 경우, 인덱스는 자동적으로 2씩 증가한다)
그러므로, 만약 P 가 문자 포인터이면, P[0] 가 배열의 첫번째 요소를 가리키는 P^ 와 같고, P[1] 은 배열의 두번째 문자이며 나머지도 동일하다.
P[-1] 은 P[0] 왼쪽에 있는 '문자' 를 가리킨다. 
컴파일러는 이런 인덱싱에는 범위체크를 하지 않는다.
 
 StrUpper() 함수는  포인터 인덱싱을 null-terminated 문자열을 통해 반복하는 형태로 구성되어 있다.
function StrUpper(Dest, Source: PChar; MaxLen: Integer): PChar;
var
  I: Integer;
begin
  I := 0;
  while (I < MaxLen) and (Source[I] <> #0) do
  begin
    Dest[I] := UpCase(Source[I]);
    Inc(I);
  end;
  Dest[I] := #0;
  Result := Dest;
end;
 
 
 
 
 
 

Mixing Delphi Strings and Null-Terminated Strings

long strings (AnsiString)과 null-terminated 문자열 (PChar) 를 표현식이나 할당 등에 섞어서 사용할 수 있으며, long-string 을 매개변수로 받는 함수에 PChar를 매개변수로 보낼 수 있다.
만약 S 가 string 이고, P 가 PChar 라면 S:=P 라는 표현식은 null-terminated 문자열을 long string 에 복사한다.
 
바이너리 연산에서, 만약 한쪽 피연산자가 long string 이고 나머지 한쪽 피연산자가 PChar 이면, PChar 는 long string 으로 변환된다.
 
PChar 는 long string 으로 캐스팅이 가능하다.
이것은 두개의 PChar 변수로 문자열 연산을 할 때 유용하다.
 
예를 들어,
S := string(P1) + string(P2);
 
 
또한 long string 을 null-terminated 문자열로 변환하는 것도 가능하다.
아래의 법칙들이 있다.
 
  • 만약 S 가 long string 이라면, PChar(S) 는 S를 null-terminated string 으로 변환된다. 이것은 S의 첫번째 문자의 포인터를 리턴한다. 예를 들어, Str1 과 Str2 가 long string 이라면, Win32 API 인 MessageBox() 함수를 다음과 같이 호출하는 것이 가능하다 : MessageBox(0, PChar(Str1), PChar(Str2), MB_OK); 
  • Pointer(S) 라는 표현식을 사용하여 long string 을 타입없는 포인터로 변환이 가능하다. 그러나 S 가 만약 비어있는 문자열이라면 해당 타입캐스팅은 nil 을 반환한다.
  • PChar(S) 는 항상 메모리블럭의 포인터를 리턴한다. 만약 S 가 비어있는 문자열이라면 포인터는 #0 을 리턴한다. 
  • long-string 변수를 포인터로 타입캐스트 했을 경우, 그 변수에 새로운 값이 할당되거나 범위를 벗어나기 전까지 그 포인터는 유효하다. 만약 다른 long-string 을 포인터로 캐스팅했을 경우, 그 포인터는 타입캐스트가 이루어진 문장 속에서만 유효하다.  
  • long-string 을 포인터로 캐스팅했을 경우, 그 포인터는 보통 읽기전용으로 간주된다. long-string 을 캐스팅한 포인터는 아래에 나열되는 조건들을 충족할 때에만 안전하게 사용이 가능하다. 
  • long-string 변수를 캐스팅한다.
  • 그 문자열은 비어있지 않다.
  • 그 문자열은 유니크하다(= 레퍼런스 카운트가 1 이다). 문자열이 유니크함을 확정하기 위해 SetLength, SetString, UniqueString 함수를 호출한다. 
  • 그 문자열은 타입캐스트가 이루어지고 난 이후 수정되지 않아야 한다. 
  • 문자의 수정은 모두 문자열 내에서 이루어져야 한다. 포인터의 범위를 벗어나지 않도록 주의해야 한다. 
 
같은 법칙이 WideString 과 PWideChar 를 함께 사용할 경우에도 적용된다.






Posted by bloodguy
,