Програмен език C++

Част 19

Библиотечни функции за стрингове

(съдържание)

В библиотеката от функции, която съпровожда C и C++ има десетки функции които работят с низове (стрингове). Те се използват за копиране на стрингове, сравнение и търсене в стрингове и т.н. В тази лекция ще се запознаем само с някои от тях за да добиете представа за техните възможности, както и с тези от тях, които ще са ни необходими в този курс. В края на лекцията ще създадем елементарен клас, който имитира стрингове.

Тези функции са описани подробно в документацията на съответния компилатор или в помощната му програма. Употребата на всички тези функции изисква заглавния файл (header file) STRING.H.

1. Намиране на дължина на низ (String Length). Функцията strlen(str) връща дължината на стринга str. Етимологията на името на функцията идва от string (низ) и length (дължина). В тази дължина не се брои нулевият символ '\0'. Програмният код, даден по-долу, ще върне дължина 11, въпреки че с нулевия символ стрингът str12 има дължина 12.

 char str12[] = "Programming";
 cout << "Length of " << str12
      << " is " << strlen(str12);

2. Копиране на стрингове (Copying Strings). Функцията strcpy(dest, src) копира стринга src в dest. Етимологията на името на функцията идва от string (низ) и copy (копирам). Преди да сме предефинирали значението на оператора за присвояване, =, не можем да го използваме във вида dest = src; -- ето защо използваме тази функция. Стрингът-назначение dest трябва да бъде с дължина по-голяма или равна на стринга-източник src , иначе ще се "загуби" нулевият символ, който определя края на стрига dest. Следният програмен код илюстрира нейното действие.

 char str12[] = "Programming";
 char newstr[20];

 strcpy(newstr, str12);
 cout << "newstr is: " << newstr;

3. Добавяне на стрингове (Appending Strings). Функцията strcat(str1, str2)добавя стринга str2 към стринга str1, при което стрингът str2 не се променя, но стрингът str1се променя и е равен на нареждането на стринговете str1 и str2 в една редица. Ето защо и функцията е кръстена така - strcat идва от string (низ) и concatenate (съединявам). Следният програмен код илюстрира нейното действие.

 char str1[20] = "Programming";
 char str2[] = " is fun!";

 strcat(str1, str2);

 cout << "str1 is: " << str1 << endl;
 cout << "str2 is: " << str2 << endl;
 cout << "Length of str1 is "
      << strlen(str1) << endl;

Стрингът str1, който събира двата стринга, трябва да бъде с дължина по-голяма от сумата от дължините на първоначалните стрингове, иначе ще се "загуби" нулевият символ, който определя края на стрига str1. В примерния програмен код дължината на str1 бе първоначално 11, а на str2 - 8 (и това без нулевия символ). Следователно общата дължина на получения съединен стрингт ще е 19 без нулевия символ или 20 с него.

4. Сравняване на стрингове (Comparing  Strings). Функцията strcmp(str1, str2) сравнява двата стринга str1 и str2 и връща  0,  ако те са еднакви, и число, различно от нула, ако са различни. При различни аргументи, върнатата стойност е отрицателна, ако str1 предхожда str2 по азбучно сортиране. И обратно, ако str1 е след str2 по азбучен ред, върнатата стойност е положителна. Етимологията на името на функцията идва от string (низ) и compare (сравнявам). Следният програмен код илюстрира нейното действие.

 char name1[] = "Maria";
 char name2[] = "Zarina";

 cout << name1 << " and Maria are "
      << strcmp(name1, "Maria") << endl;

 cout << name2 << " and Maria are "
      << strcmp(name2, "Maria") << endl;

 cout << "Maria and " << name2 <<  " are "
      << strcmp("Maria", name2) << endl;

Резултатът от изпълнение на този код е следният:

Maria and Maria are 0
Zarina and Maria are 13
Maria and Zarina are -13

Има още няколко функции за сравняване на стрингове, които имат по-различни изисквания. Например, stricmp(str1, str2) сравнява двата стринга str1 и str2 без да прави разлика между големи и малки букви: i идва от insensitive - нечувствителен (към главни и малки букви).

Аргументите на повечето функции, които работят със стрингове са указатели (pointers) и затова запознаването с тях е затруднено на този етап, преди да сме се изучили указателите.

5. По-елегантния низ от клас string. С така наречените Си-низове (C-strings) трябва да използваме бибиотечни функции, вместо много по-разбираемите операции  от вида str1 + str2 или str1 == str2. В C++ има клас (templatized class) за боравене с низ от символи и името му е string. За да използвате този клас трябва да включите заглавния файл, който го поддържа с препроцесорната директива #include <string>. Ето една кратка програма, която показва по-директния (и по-разбираем) подход в програмирането, когато се използва класа string.

// cppstr.cpp
// test program for string class of C++

#include <iostream.h>
#include <string>           // for string class
#include <conio.h>          // for getche()

void main()
{
   string s1, s2 = "Cpp string";

  // you can assign value of one string to another
   s1 = s2;
   cout << "s1 is: " << s1 << endl;

   cout << "\nLength of "
        << s1
        << " is "
        << s1.length()  // get the string length
        << endl;

   // you can assign a string constant to a string
   s1 = " is better!";
   cout << "s1 is: " << s1 << endl;

   // you can append two strings
   s1 = s2 + s1;
   cout << "s1 is: " << s1 << endl;

   // you can compare two strings
   s1 = "Maria";
   s2 = "Zarina";

   cout << (s1 == s2) << endl;
   cout << (s1 == "Maria") << endl;
   cout << (s1 == "Zarina") << endl;

   // stop the flow on the monitor
   getch();
}  //end main

Въпреки, че този клас е изключително удобен, ние ще продължим да използваме Си-стринговете в следващите лекции, за да можем сами да създадем с тяхна помощ клас, който имитира класа string.

6. Потребителски създаден клас за стрингове (A Homemade String Class). Отново ще използваме пример, който е взет от книгата на Лафоор [1]. Според Лафоор мотивацията за създаване на такъв клас произлиза от неспособността на Си-стринговете  да извършват следните лесно интерпретируеми операции:

1. Да дефинират променлива, така както се дефинира променлива от предефинираните типове: за С-стринговете трябва да се дефинира масив от символи.

2. Не могат да се копират С-стрингове с оператора за присвояване, =.

3. Функциите, които работят със Си-стрингове, не предупреждават когато се надмине дължината на стринга.

4. Не могат да се съединяват два Си-стринга с оператора +;

5. Не могат да се сравняват два стринга със стандартните логически оператори ==, !=, <, и >.

В дадения пример ще бъдат решени първите два проблема, а в следващите лекции на курса, когато се изучи предефенирането на оператори (overloading of C++ operators) ще бъдат решени и следващите проблеми.

Новият клас е с име xString и има една променлива str, която е масив от символи с максимална големина, определена от външната константа MAX. В класа има четири функции - init(), input(), display() и append(). Първата инициализира стринга, с втората вкарваме символи в него от клавиатурата, трета го показва на екрана, и чрез четвъртата се съединяват два стринга. Ето и примерната програма, която поддържа този клас:

/ strclass.cpp
// uses a class to model strings

#include <iostream.h>
#include <string.h>           // for strlen(), strcpy(), etc.
#include <conio.h>            // for getche()

const int MAX = 80;           // maximum length of xStrings

class xString
{
   private:
      char str[MAX];          // ordinary C string

   public:
      void init(char s[])     // initialize with string
      {
        strcpy(str, s);
      }

      void input()            // get string from user
      {
         cin.get(str, MAX);
      }

      void display()          // display string
      {
         cout << str;
      }

      void append(xString xs)   // append argument string
      {
         if (strlen(str) + strlen(xs.str) < MAX)
            strcat(str, xs.str);
         else
            cout << "\nError: xString too long" << endl;
      }
};

void main()
{
   xString s1, s2, s3;         // make xString objects

   s1.init("Greetings, ");     // initialize s1

   cout << "Enter your name: ";
   s2.input();                 // get s2 from user

   s1.append(s2);              // append s2 to s1
   s3 = s1;                    // set s3 to s1
   s3.display();               // display s3

   // stop the flow on the monitor
   getch();

}  //end main

В програма се използват три от вече изучените библиотечни функции за работа със Си-стрингове: strcpy(), strlen() и strcat(): те са във функциите на класа xString.

В главната функция се създават три стринга от тип xString - s1,s2 и s3. Тяхното дефиниране е ясно и разбираемо, понеже е същото като дефинирането на променливи от предефинираните типове, например променливи от тип int.

След това първият стринг се инициализира със стринговата константа "Greetings, " като се използва функцията на класа init(). Функцията има аргумент от тип масив от символи, т.е. Си-стринг. В следващите лекции ще изучим конструкторите на класовете (constructors) и ще видим още по-елегантно и безгрешно инициализиране на обекти.

По-нататък в главната функция, съдържанието на втория стринг се въвежда от клавиатурата с помощта на функцията input(). След това двата стринга s1 и s2 се свързват заедно с  функцията append(): според логиката на тази функция свързаният стринг замества s1 , тъй като точно този обект извиква тази функция.

В следващия програмен ред става присвояването на съдържанието на s1 на s3 и третият стринг се показва на екрана с функцията display(). Това присвояване става с оператора за присвояване, s3 = s1;. Този оператор, както споменахме по-горе, не работи със Си-стрингове, но работи с обекти: просто обектите на един клас са еднакви по памет и структура и програмата копира съдържанието на единия обект в съдържанието на другия.

Друга особеност на този клас е защитата от препълване на масива от символи - тя фигурира във функцията на класа input(), чрез използване на cin.get(str, MAX), както и във функцията на класа append(), където се извършва проверката if (strlen(str) + strlen(xs.str) < MAX). (В това неравенство е единствената промяна на програмата от оригиналната на Лафоор, като сме заменили MAX - 1 с MAX.)

Това представяне на стринг чрез този клас си има и своята отрицателна страна - винаги се резервира памет за 80-те символа във всеки един създаден обект. По-късно в този курс ще намерим решение на този проблем, чрез използване на динамичната памет.

(съдържание)
.
Литература
.

[1] Robert Lafore; C++ Interactive Course. Waite Group Press, Macmillan Computer Publishing, 1996.
.
Автор: Проф. Процесор Аритметиков
.
[ това е материал от брой 20 от юни 2008 г. на списание "Коснос" www.kosnos.com ]

Keywords: С++,  OOP programming , C++ , Classes , Inheritance , Reusability , Creating New Data Types , Polymorphism and Overloading
Ключови думи: клас , обект, обектно ориентирано програмиране , полиморфизъм switch if else ?