Строки
Free Pascal Reference Guide
Free Pascal Standard Units Reference Manual
В.Д.Далека и др. "Модели и структуры данных"
Строка - это линейно упорядоченная последовательность символов, принадлежащих конечному множеству символов, называемому алфавитом. Строки обладают следующими важными свойствами:
- их длина, как правило, переменна, хотя алфавит фиксирован;
- обычно обращение к символам строки идет с какого-нибудь одного конца последовательности, т.е. важна упорядоченность этой последовательности, а не ее индексация; в связи с этим свойством строки часто называют также цепочками;
- чаще всего целью доступа к строке является не отдельный ее элемент (хотя это тоже не исключается), а некоторая цепочка символов в строке.
Говоря о строках, обычно имеют в виду текстовые строки - строки, состоящие из символов, входящих в алфавит какого-либо выбранного языка, цифр, знаков препинания и других служебных символов. Однако, следует иметь в виду, что символы, входящие в строку могут принадлежать любому алфавиту. Если использовать алфавит {0, 1}, то строки, составленные из таких однобитных символов, называются битовыми строками. И к битовым, и к символьным строкам применяют сходные операции: определение длины, конкатенация, поиск подстроки и т.п.
Существуют несколько вариантов представления строк в памяти. Наиболее часто используются:
- представление строк вектором переменной длины с признаком конца,
- представление строк вектором переменной длины со счетчиком.
Представление строк в виде векторов, принятое в большинстве универсальных языков программирования, позволяет работать со строками, размещенными в статической памяти. Кроме того, векторное представление позволяет легко обращаться к отдельным символам строки как к элементам вектора - по индексу.
Признак конца - это особый символ, принадлежащий алфавиту (таким образом,
полезный алфавит оказывается меньше на один символ), и занимает то же количество
разрядов, что и все остальные символы. Чаще всего в качестве маркера конца
строки используется символ с ASCII-кодом 0. Представление строк вектором
с нулевым завершающим байтом (обозначается null terminated string или
ASCIIZ-строка) требует памяти на один байт больше длины строки.
'H' |
'E' |
'L' |
'L' |
'O' |
#0 |
Счетчик символов - это целое число, и для него отводится достаточное количество
битов, чтобы их с избытком хватало для представления длины самой длинной строки,
какую только можно представить в данной машине. Обычно для счетчика отводят от 8
до 32 битов. При использовании счетчика символов возможен произвольный доступ к
символам в пределах строки, поскольку можно легко проверить, что обращение не
выходит за пределы строки. Счетчик размещается в таком месте, где он может быть
легко доступен - начале строки или в дескрипторе строки. Максимально возможная
длина строки, таким образом, ограничена разрядностью счетчика.
#5 |
'H' |
'E' |
'L' |
'L' |
'O' |
И счетчик символов, и признак конца могут быть доступны для программиста как элементы вектора.
В Паскале стандартный строковой тип (string) представляется в виде массива символов, индексация в котором начинается с 0. Однобайтный счетчик числа символов в строке является нулевым элементом этого массива. Максимальная длина такой строки - 255 символов.
В языке Си типы данных максимально приближены к тем типам, с которыми работают машинные команды. Поскольку машинные команды не работают со строками, в Си нет специального типа данных для строк. Строки в Си представляются в виде обычных массивов символов и включают признак конца - нулевой байт (ASCIIZ-строки). Операции над строками выполняютя как операции обработки массивов. Несомненным достоинством ASCIIZ-строк является то, что их максимальный размер, как правило, ограничен лишь ресурсами системы.
В расширенных вариантах языка Паскаль (Turbo Pascal, Free Pascal, Delphi) также есть возможность работать с ASCIIZ-строками, для этого используется тип PChar. Фактически, переменные этого типа являются указателями на первый элемент вектора, содержащего ASCIIZ-строку:
Type PChar=^Char;
Как и любому другому указателю, переменным типа PChar значение можно задать:
- резервированием памяти при помощи процедур New или GetMem;
Var P:PChar; i:integer; Begin GetMem(P,1024); for i:=0 to 1022 do read(P[i]); P[1023]:=#0; End.
- присваиванием адресов существующих объектов (оператор @, функции Addr и Ptr).
Var S:String; P:PChar; Begin S := 'It will be a null-terminated string soon.'#0; P := @S[1]; End.
В Turbo Pascal полноценная работа с ASCIIZ-строками возможна только при включенном расширенном синтаксисе: {$X+}. Это позволяет использовать следующие возможности компилятора:
- автоматическая конвертация строковых констант в тип PChar;
- вывод содержимого ASCIIZ-строки в файл типа text;
- обращение к переменной типа PChar, как к массиву (array [0..n] of char), и наоборот (использование массива вместо PChar).
Var P:PChar; i:integer; P1:array [0..1023] of char; Begin P:='This is a null-terminated string.'; Writeln(P); Writeln(P[0]); for i:=0 to 1022 do read(P1[i]); P1[1023]:=#0; Writeln(PChar(P1)); P:=P1; End.
Для выполнения различных операций над ASCIIZ-строками предназначены функции модуля Stings. Вот специфические для Паскаля функции, связанные с конвертированием строк между различными представлениями:
uses Strings; function StrPas(p:PChar):String; function StrPCopy(dest:PChar; src:String):PChar;
Чтобы перевести строку из представления String в PChar, можно
- воспользоваться тем же буфером, в котором хранится String:
Var S:String; P:PChar; (* ... *) S[Length(S)+1]:=#0; {записываем символ-ограничитель} P:=@S[1]; {получаем указатель на первый символ в строке} - скопировать строку в новый буфер при помощи функции StrPCopy,
при этом необходимо дополнительно позаботиться о резервировании достаточного
количества памяти:
Var S:String; P:PChar; (* ... *) GetMem(P,Length(S)+1); {В Free Pascal - P:=StrAlloc(Length(S)+1);} StrPCopy(P,S);
Наиболее простой способ перевести строку из представления PChar в String - воспользоваться функцией StrPas. Следует иметь в виду, что символы после 255-го будут отброшены (это максимальная длина строки в представлении String);
Для определения длины ASCIIZ-строки используется функция StrLen.
Она возвращает количество символов в строке без завершающего нуля.
uses Strings; function StrLen(p:PChar):SizeInt; | #include <string.h> size_t strlen(const char *p); |
Приведённые ниже примеры на Си расчитаны на возможность записи в строковые константы. В GCC ver. 3.x можно использовать опцию -fwritable-strings. В поздних версиях GCC эта возможность недоступна.
В отличие от строк со счетчиком (string) простое присваивание ASCIIZ-строк
не означает копирования их содержимого:
uses Strings;
Var P1:PChar;
Const P2:PChar='Some string';
Begin
P1:=P2; {просто дублирование указателя на строку}
P2[1]:='a';
Writeln(P1); {выводится "Same string"}
End.
|
#include <string.h>
#include <stdio.h>
char *P1;
char *P2="Some string";
int main() {
P1=P2;
P2[1]='a';
puts(P1);
}
|
Для копирования содержимого ASCIIZ-строк используется ряд функций. Функция
StrNew резервирует необходимое количество динамической памяти под
копию указанной строки и копирует туда ее содержимое. Чтобы освободить память
необходимо воспользоваться процедурой StrDispose. Для копирования
строки src в предварительно зарезервированный буфер dest используются функции
StrCopy и StrLCopy, при этом необходимо помнить, что
переполнение буфера не проверяется, однако вторая функция позволяет указать
максимальное количество копируемых символов maxlen, превысив которое остальные
символы исходной строки будут отброшены. Функции StrCopy и StrLCopy
работают некорректно, если dest и src перекрываются. В таком случае необходимо
использовать функцию StrMove, ее третий параметр - точное количество
копируемых символов.
uses Strings; function StrNew(p:PChar):PChar; procedure StrDispose(p:PChar); function StrCopy(dest,src:PChar):PChar; function StrLCopy(dest,src:PChar; maxlen:Intege):PChar; function StrMove(dest,src:PChar; len:SizeInt):PChar; | #include <string.h> char* strdup(const char *p); void free(void *p); /* из stdlib.h */ char* strcpy(char *dest, const char *src); char* strncpy(char *dest, const char *src, size_t maxlen); void* memmove(void *dest, const char *src, size_t len); |
В Паскале ASCIIZ-строки, в отличие от обычных строк, нельзя складывать.
Вместо этого используются функции конкатенации StrCat и StrLCat.
Эти функции в конец строки, определяемой первым параметром, дописывают
содержимое второго параметра-строки. Программист должен позаботится о том,
чтобы в буфере dest было достаточное количество места под композицию обоих строк
и завершающий нулевой символ. Функция StrLCat проверяет, чтобы длина
строки dest после конкатенации не превысила maxlen, при необходимости отбрасывая
лишние символы.
uses Strings; function StrCat(dest,src:PChar):PChar; function StrLCat(dest,src:PChar; maxlen:SizeInt):PChar; | #include <string.h> char* strcat(char *dest, const char *src); char* strncat(char *dest, const char *src, size_t maxlen); |
В приведенном примере сначала дублируется строка P, причем адрес нового
буфера сохраняется в P1. Затем внутри новой копии перемещается подстрока
"text", так что получается строка "Some text text" (если перемещать не 5, а 6
символов, будет скопирован завершающий нуль, тогда новая строка будет
интерпретироваться как "Some text"). После чего исходная строка копируется в
статический буфер P2. И, наконец, в конец этого буфера дописывается измененная
строка из P1. В результате на экран выводится "Some long textSome text text".
Uses Strings; Var P1:PChar; P2:array [0..63] of char; Const P:PChar='Some long text'; Begin P1:=StrNew(P); StrMove(P1+4,P1+9,5); StrCopy(P2,P); StrCat(P2,P1); Writeln(P2); End. |
#include <string.h>
#include <stdio.h>
char *P1;
char P2[64];
char *P="Some long text";
int main() {
P1=strdup(P);
memmove(P1+4,P1+9,5);
strcpy(P2,P);
strcat(P2,P1);
puts(P2);
}
|
Для сравнения ASCIIZ-строк необходимо использовать функции серии Str*Comp.
Функции возвращают 0, если строки совпадают, или целочисленный результат
лексикографического сравнения. StrLComp и StrLIComp
сравнивают только первые maxlen символов. StrIComp и StrLIComp
выполняют сравнение без учета регистра буквенных символов.
|
uses Strings; function StrComp(str1,str2:PChar):SizeInt; function StrIComp(str1,str2:PChar):SizeInt; function StrLComp(str1,str2:PChar; maxlen:SizeInt):SizeInt; function StrLIComp(str1,str2:PChar; maxlen:SizeInt):SizeInt; |
#include <string.h> int strcmp(const char *str1, const char *str2); int strcasecmp(const char *str1, const char *str2); int strncmp(const char *str1, const char *str2, size_t maxlen); int strncasecmp(const char *str1, const char *str2, size_t maxlen); |
В следующем примере программа сравнивает первый параметр командной строки
с подстроками "add" и "sub" и в случае совпадения выполняет соответствующие
действия.
Uses Strings; Var P:PChar; S:String; Begin S:=ParamStr(1); S[Length(S)+1]:=#0; P:=@S[1]; if StrComp(P,'add')=0 then begin (* ... *) end; if StrComp(P,'sub')=0 then begin (* ... *) end; End. |
#include <string.h>
char *P;
int main(int argc, char **argv) {
P=argv[1];
if (!strcmp(P,"add")) {
/* ... */
}
if (!strcmp(P,"sub")) {
/* ... */
}
}
|
Поиск подстроки str2 в строке str1 осуществляется функциями StrPos
и StrIPos. Функции возвращают указатель на первое вхождение подстроки
str2 внутри str1 или nil, если подстрока не найдена. StrIPos выполняет
поиск без учета регистра. Для поиска одиночного символа в строке используются
функции StrScan (поиск первого (самого левого) вхождения символа c
в строке p) и StrRScan (поиск последнего (самого правого) вхождения
символа c в строке p).
uses Strings; function StrPos(str1,str2:PChar):PChar; function StrIPos(str1,str2:PChar):PChar; function StrScan(p:PChar; c:Char):PChar; function StrRScan(p:PChar; c:Char):PChar; | #include <string.h> char* strstr(const char *str1, const char *str2); char* strcasestr(const char *str1, const char *str2); char* strchr(const char *p, int c); char* strrchr(const char *p, int c); |
Приведенный фрагмент после манипуляций со строками выводит текст "Some text".
Uses Strings; Var P1,P2:PChar; Const P:PChar='Some long text'; Begin P1:=StrPos(P,'long'); P2:=StrPos(P,'text'); if (P1<>nil) and (P2<>nil) then StrMove(P1,P2,5); Writeln(P); End. |
#include <string.h>
#include <stdio.h>
char *P1, *P2;
char *P="Some long text";
int main() {
P1=strstr(P,"long");
P2=strstr(P,"text");
if (P1 && P2) memmove(P1,P2,5);
puts(P);
}
|
Описанная ниже функция считает количество пробелов в строке.
Function SpaceCount(p:PChar):integer; Var ptr:PChar; i:integer; Begin i:=0; ptr:=p; repeat ptr:=StrScan(ptr,' '); if ptr<>nil then begin inc(i); inc(ptr); end; until (ptr=nil) or (ptr[0]=#0); SpaceCount:=i; End; |
int SpaceCount(char *p)
{
int i=0;
char *ptr=p;
do {
ptr=strchr(ptr,' ');
if (ptr) {
i++;
ptr++;
}
} while (ptr && *ptr);
return i;
}
|
