beesoft.org

Lancuchy tekstowe (string).

Typ string nienalezy wprawdzie do STL, jest jednak wazna czescia biblioteki standardowej C++. Nie ma praktycznie rzecz biorac duzego programu, ktory w taki czy inny sposob by nie uzywal lancuchow tekstowych. Klasa string uzywa wewnetrznie takiego samego mechanizmu obslugi tablic typu char, jak to sie odbywa w C, poprzez wskaznik char*. Jednakze cale zarzadzanie pamiecia odbywa sie automatycznie, w tle.

Porownajmy dwa nastepujace programy, w ktorym z tekstow zawierajacych 'imie' i 'nazwisko' tworzymy tekst 'osoba' zawierajacy obie te dane.

Program w wersji C:
#include <iostream>
#include <string.h>
using namespace std;

int main()
{
   const char* const imie     = "Piotr";
   const char* const nazwisko = "Pszczolkowski";

   char* osoba = new char[ strlen( imie ) + 1 + strlen( nazwisko ) + 1 ];
   strcat( strcat( strcpy( osoba, imie ), " " ), nazwisko );

   cout << "Dane osoby: " << osoba << endl;
   return 0;
}

Program w wersji uzywajacej klasy string:
#include <iostream>
#include <string>
using namespace std;

int main()
{
   const string imie     = "Piotr";
   const string nazwisko = "Pszczolkowski";

   string osoba = imie + " " + nazwisko;

   cout << "Dane osoby: " << osoba << endl;
   return 0;
}

Typ string moze byc uzywany podobnie jak typy wbudowane int, double itd. Niektore operatory jak np. '+' dla dla string przeladowane.

Biblioteka standardowa udostepnia typ string poprzez typedef.

   typedef basic_string<char> string;
Co oznacza, ze tak naprawde bedziemy uzywac szablonu klasy basic_string, ktory jest zadaklarowany w nastepujacy sposob:
   template< typename _charT, typename _Traits = char_traits<_charT>, typename _Alloc = allocator<_charT> >
      class basic_string;

Pierwszy parametr _charT okresla typ uzywanych znakow. Dla zwyklych lancuchow tekstowych zostanie przekazany wbudowany typ char. Mozna takze przekazac typ np. wchar_t, co pozwala na obsluge zestawow poszerzonych znakow (16 bitowych).

Drugi parametr _Traits jest klasa pomocnicza, mowiaca klasie basic_string jak traktowac nalezy poszczegolne znaki, np. jak je porownywac. W ten sposob cechy charakterystyczne obslugiwanego zestawu znakow mozna umiescic poza klasa basic_string, co uniezaleznia ja od specyfiki stosowanego wlasnie zestawu.

Trrzeci parametr _Alloc odpowiedzialny jest za zarzadzanie pamiecia.


1. Wlasciwosci kontenera.

String mozna traktowac jako kontener dla znakow. Posiada wiele podobienst do klasy kontenerowej vector. Jednakze stosowanie vector<char> ma sens tylko w przypadku gdy chcemy przetwarzac poszczegolne znaki, natomiast string powolano do zycia po to aby moc latwo i przyjemnie przetwarzac cale ciagi znakow, lancuchy znakowe.

Klasa string spelnie zarowno wymagania ogolne dotyczace kontenerow, jak rowniez wymagania dotyczace kontenerow dwukierunkowych. Spelnia rowniez wymagania dotyczace kontenerow sekwencyjnych wlacznie z wymaganiami opcjonalnymi push_back(), operator[] jak rowniez at().

Ze wzgledu na wlasnosci kontenera string i faktu, ze iteratory tej klasy sa iteratorami dostepu swobodnego (and. random access iterators) w odniesieniu do klasy string mozna uzywac algorytmow biblioteki standardowej. Demonstruje to ponizszy program:

#include <cctype>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

char to_upper( char znak )
{
   return toupper( znak );
}

int main()
{
   const char text[] = "Miod to jest to co misie lubia najbardziej.";

   //. 1
   string str( text, text + sizeof( text ) - 1 );
   cout << str << endl;

   // 2. Globalny algorytm wyszukiwania
   string::iterator it = find( str.begin(), str.end(), '.' );
   if( it != str.end() ) {
      cout << "Kropke znaleziono na pozycji: " << ( it - str.begin() ) << endl;
   }

   // 3. Globalny algorytm transformujacy
   transform( str.begin(), str.end(), str.begin(), to_upper );

   // 4. Globalny algorytm odwracania
   reverse( str.begin(), str.end() );
   cout << "Odwrocony tekst: " << str << endl;
   reverse( str.begin(), str.end() );

   // 5. Globalny algorytm zastepowania
   replace( str.begin(), str.end(), ' ', '*' );
   cout << "Spacje zamieniono na gwiazdki: " << str << endl;

   // 6. Globalny algorytm sortowania.
   sort( str.begin(), str.end() );
   cout << "Posortowane: " << str << endl;

   return 0;
}

Powyzszy program wyswietli na ekranie ponizsze informacje:
Miod to jest to co misie lubia najbardziej.
Kropke znaleziono na pozycji: 42
Odwrocony tekst: .JEIZDRABJAN AIBUL EISIM OC OT TSEJ OT DOIM
Spacje zamieniono na gwiazdki: MIOD*TO*JEST*TO*CO*MISIE*LUBIA*NAJBARDZIEJ.
Posortowane: *******.AAABBCDDEEEIIIIIJJJLMMNOOOORSSTTTUZ

2. Typ size_type.

Typem numerycznym używanym przy określaniu pozycji znaku (indeks) w łańcuchu, jak i przy opisie rozmiarów, jest typ size_type.
Jest to typ całkowito-liczbowy bez znaku. Rozmiar typu jest zależny od implementacji.

Taki typ ma stała o nazwie npos.

static const size_type	npos = static_cast<size_type>(-1);
Stała ta pełni dwojaką rolę.
W przypadku określania zakresów znaków jej podanie oznacza "wszystkie znaki".
W przypadku funkcji wyszukujących, zwrot tej wartości oznacza - "nie znaleziono".


3. Konstruktory.

Poniżej przedstawiam możliwe konstruktory klasy string.
Są to wersje o tyle uproszczone, że nie pokazują parametru alokatora, który jeśli nie zostanie podany przez użytkownika, zostanie zastąpiony wywołaniem alokatora domyślnego.

  1. string();
  2. string( const char* tablica_znakowa );
  3. string( const char* tablica_znakowa, size_type n );
  4. string( size_type n, char znak );
  5. string( const string& inny_string );
  6. string( const string& inny_string, size_type start_pos, size_type n = npos );
  7. template<class InputIterator> string( InputIterator beg, InputIterator end );

Poniższa tablica pokazuje przykłady użycia każdego z konstruktorów.

Nr Konstruktor Wartość Opis
1 string s1; Łańcuch pusty
2 string s2( "abcxyz" ); abcxyz Inicjacja łańcucha tablicą znakową.
3 string s3( "abcxyz", 3 ); abc Inicjacja łańcucha 3-ma początkowymi znakami tablicy znakowej.
4 string s4( 5, 'X' ); XXXXX Inicjacja łańcucha pięcioma znakami X.
5 string s5( s2 ); abcxyz Inicjacja łańcucha innym łańcuchem.
6a string s6a( s2, 3 ); xyz Inicjacja łańcucha innym łańcuchem począwszy od pozycji nr 3.
6b string s6b( s2, 3, 2 ); xy Inicjacja łańcucha 2-ma znakami innego łańcucha począwszy od pozycji nr 3.
7 string s7( s2.begin(), s2.end() ); abcxyz Inicjacja łańcucha innym łańcuchem przy użyciu iteratorów.

Należy zwrócić uwagę, że nie można zainicjować łancucha jednym znakiem:

string s8( 'X' );	// Błąd

Wyjściem z tej sytuacji jest:

string s8( 1, 'X' );	// OK


4. Rozmiary.

Aby sprawdzić maksymalną liczbę znaków w łańcuchu, którą dopuszcza nasza implementacja, możemy użyć funkcji:

size_type max_size() const;
Złożoność algorytmiczna tej funkcji jest stała O(1).

Aby otrzymać liczbę znaków które aktualnie zawiera łańcuch, możemy użyć jedną z poniższych funkcji:

size_type size() const;
size_type length() const;
Obie funkcje zwracaja tę samą liczbę, czyli liczbe znaków w łańcuchu. Wynik odpowiada end() - begin().
Złożoność algorytmiczna tych funkcji jest stała O(1) (dla kontenera string).

Aby sprawdzić czy łańcuch jest pusty, czy nie zawiera żadnych znaków, używamy funkcji:

bool empty() const;
Funkcja zwraca wartosci 'true', jeśli łańcuch niezawiera żadnych znaków.
Złożoność algorytmiczna jest stała O(1).
Pomimo że 'empty()' i 'size() == 0' zwracają ten sam wynik, zalecane jest używanie 'empty()'.
Chodzi po prostu o wyrobienie sobie nawyku. W przypadku innych kontenerów złożoność algorytmiczna funckji 'size()' nie musi być stała.
Contact: piotr@beesoft.org
(C) 2006-2008 beesoft.org
Last modification date: 2008-06-29
Visitis counter:
counter of visits