Низ: Разлика между версии

от Уикипедия, свободната енциклопедия
Изтрито е съдържание Добавено е съдържание
Quasarsz (беседа | приноси)
Добавен C# до String Builder
Quasarsz (беседа | приноси)
м Поправка на референция
Ред 152: Ред 152:
</source></big>
</source></big>
===Операции със символни низове===
===Операции със символни низове===
Трябва да се отбележи, че символните низове са неизменими и всяка промяна на един низ води до създаване на нов, в който се пази резултата и пренасочване към него. Това може да силно да забави изпълнението на една програма при много редакции на един низ (например долепване на символи в цикъл). В такива случаи използваме класа [[:String Builder]] който оптимизира работата с низове.<br><br>
Трябва да се отбележи, че символните низове са неизменими и всяка промяна на един низ води до създаване на нов, в който се пази резултата и пренасочване към него. Това може да силно да забави изпълнението на една програма при много редакции на един низ (например долепване на символи в цикъл). В такива случаи използваме класа String Builder, който оптимизира работата с низове.<br><br>
'''Сравняване на низове'''
'''Сравняване на низове'''
:'''Сравняване за еднаквост.''' Удобен за това е метода '''Equals()''', който работи еквивалентно на ''''==''''. Метода връща булев резултат ''''true'''' при еднакви низове и ''''false'''' при различни. Трябва да се има в предвид, че по този начин се сравняват побуквено елементите на масива, като се прави разлика между главни и малки букви. Често ще ни интересува само текстовото съдържание без регистъра на буквите, затова може да използваме метода ''''Equals()'''' с параметър ''''StringComparison.CurrentCultureIgnoreCase''''
:'''Сравняване за еднаквост.''' Удобен за това е метода '''Equals()''', който работи еквивалентно на ''''==''''. Метода връща булев резултат ''''true'''' при еднакви низове и ''''false'''' при различни. Трябва да се има в предвид, че по този начин се сравняват побуквено елементите на масива, като се прави разлика между главни и малки букви. Често ще ни интересува само текстовото съдържание без регистъра на буквите, затова може да използваме метода ''''Equals()'''' с параметър ''''StringComparison.CurrentCultureIgnoreCase''''

Версия от 09:41, 22 август 2013

В компютърните науки низ е крайна поредица от символи. Елементите му евентуално могат да бъдат променяни, както и дължината му. Низовете обикновено се разглеждат като тип данни и често са имплементирани като масиви от байтове (или думи), които съхраняват последователност от елементи, използвайки кодиране на символите.

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

Дължина на низ

Въпреки че низовете могат да имат произволна (но ограничена) дължина, в повечето езици за програмиране дължината е ограничена до изкуствен максимум. По принцип има два вида низови: с фиксирана дължина - имат постоянна максимална дължина и използват същото количество памет независимо дали този максимум е достигнат; низове с променлива дължина.

Формална теория

Нека означим със Σ азбуката, която е непразно крайно множество. Елементите на Σ се наричат букви. Низ (или дума) от Σ (Σ*) е всяка крайна последователност от буквите на Σ. Например ако Σ = {0, 1}, то 0101 е низ на азбуката Σ.

Множеството от всички низове над Σ с дължина n се записва Σn. Пример: ако Σ = {0, 1}, тогава Σ2 = {00, 01, 10, 11}. Имайте предвид, че Σ0 = {ε} за всяка азбука Σ.

Множество от низове над Σ (например всяко подмножество от Σ*) се нарича формален език над Σ. Пример: ако Σ = {0, 1}, множеството от низове с равен брой нули и единици ({ε, 1, 00, 11, 001, 010, 100, 111, 0000, 0011, 0101, 0110, 1001, 1010, 1100, 1111, ...}) е формален език над Σ.

Кодиране на символи

Низовите типове данни в миналото са използвали един байт за символ. Някои езици като китайски, японски и др. (познати още като CJK) се нуждаят от много повече от 256 символа. Едно от решенията на този проблем е запазването на еднобайтовото представяне на ASCII и използване на двубайтово за CJK езиците. Това довежда до проблеми със съпоставянето на низовете, загуби на дължина и др. Unicode е опростил нещата значително в това отношение и повечето програмни езици го поддържат.

Низове в PHP

В PHP символът един байт, т.е., съществуват точно 256 възможни символа. Това също означава, че PHP няма естествена поддръжка за Unicode. Знаците в даден низ могат да бъдат достъпвани и изменяни чрез указване на отместване от нулата за желания знак след низа посредством квадратни скоби, например $str[42], така че бихте могли да мислите за даден низ като за масив от знаци.

Дeфиниране и инициализиране

$txt="Hello world!";
echo $txt;

Полезни функции и оператори

Конкатенация

$txt1="Hello world!";
$txt2="What a nice day!";
echo $txt1 . " " . $txt2;

strlen() function - връща дължината на стринга

echo strlen("Hello world!");

strpos() - за търсене на символ/и в низ

echo strpos("Hello world!","world");

Низове в C

Низът в езика С представлява последователност от символи, които завършват с терминираща нула ('\0'). Използва се ASCII [[1]] таблица за символи. Няма запазена дума "string", затова трябва да използваме ето тези конструкции:

char str[13] = {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd','!', '\0'};

В този пример се вижда какво представлява стринга отвътре. Забележете, че трябва да се остави място за терминиращата нула, защото благодарение на нея се разбира кога сме достигнали края на низа. Тази конструкция може да се запише и съкратено по този начин:

char str[] = "Hello world!";

В този пример макар да не записваме терминиращата нула компилаторът си оставя място за нея и я слага.

Ето още един пример, но този път с указател(pointer)

char* str = "Hello world!";

Тук указателят запазва адреса на първият символ, а останалите символи са следващите, докато не се достине до '\0'.

В C има библиотека предназначена за работа със стрингове. За да я добавим трябва да поставим това отгоре на файла:

#include <string.h>

В тази библиотека са направени голямо количество от инструкции за работа със стрингове.

#include <string.h>

int strlen(char* str);                 //Връща дължината на низа.
char* strcpy(char* dest, char *src);   //Копира един низ в друг. "Src" се копира в "dest". 
int strcmp(char* str1, char* str2);    //Сравнява двата низа и връща 0 ако са еднакви, 
                                       //отрицателна стойност ако "str1" е по-голям, и положителна ако "str2" е по-голям.
char* strcat(char* dest, char* src);   //Добавя (append) "src" в "dest".
char* strstr(char* str, char* substr); // Търси дали в "str" се съдържа низ "substr" и връща указател сочещ към мястото кадето започва.
                                       // Връща NULL, ако резултат не е намерен.

Още за библиотеката "string.h" можете да намерите [[2]]

Друга полезна библиотека е "stdlib.h", благодарение на нея може да превръщаме стрингове в числа.

int atoi(const char *str);    //Връща целочислено число.
double atof(const char *str); //Връща реално число.

Още за библиотеката "stdlib.h" можете да намерите [[3]]

Ето пример за конкатерине на низове:

char* str1 = strdup("Hello");           //Друг начин за инициализиране.
char* str2 = malloc(strlen (str1) + 8); // Заделяме памет с malloc, колкото дължината на предишния низ + допълнително.
strcpy(str2 , str1 );                   // Копиране на str1 в str2.
strcat(str2 , " world!");               // Конкатениране на получения низ с " world!".
free(str1);                             // Освобождаване на първия низ.

// Можем освен това да пренасочваме и указателите ако искаме стойноста да е в str1. С "str1 = str2;", 
// но тогава ще е добре и да освободим и памета от str2 с ключовата дума free.

Низове в C++

В С++ съществува клас, намиращ се в "namespace std", който поддържа работа с низове. Думата "string" е ключова дума. Запазването на стрингове в памета е по начина по който се запазват и в C(накрая има терминираща нула). Работата със стрингове е много по-удобна, защото са предефинирани аритметичните знаци "+" и "=".

string str = "Hello world!";             // Ако сме инпортнали std.
std::string str2 = "Hello again world!"; // Aко не сме инппортнали std.

Можем лесно да конкатениране низове:

string str1 = "Hello";
string str2 = " world!";
string str3 = str1 + str2; // Копираме два стринга и ги даваме на трети.
str1 += str2; // Добавяне към първия стринг.

Освен това, класът стринг поддържа и много допълнителни помощни функции.

string str = "Hello world!";
str.size();
str.length(); // И двете функции показват дължината на низа (size == length).
str.empty();  // Връща 0 ако не е празен, а ако е празен връща 1.
str.begin();  // Връща указетел към началото на низа.
str.end();    // Връща указетел към кращ на низа.

Има вградена функция за размяната на низове.

string str1 = "first";
string str2 = "second";
str1.swap(str2); // Разменяне на низовете.

Още методи в класът "string" можете да намерите [[4]].

Символни низове в C#

В C# символните низове са дефинирани в класа System.String. За декларация по време на писане на код се използва служебната дума 'string'. Тъй като string е клас, той е референтен тип данни и в стека на програмата се съхранява само указателя, сочещ към поле от динамичната памет, където се съхранява реалната стойност на обекта..[1]

Създаване на String

За да работим с променлива от тип string трябва първо да я създадем и инициализираме. Трябва да се има в предвид, че създаването на референтен тип само заделя част от динамичната памет със стойност null. Ако се опитаме да извикаме инстанцирания обект, ще получим грешка за достъп до липсваща стойност NullReferenceException. Има три начина за инициализиране на променливи:
Задаване на литерал за символен низ. По този начин задаваме предефинирано текстово съдържание на променливата. Използва се когато знаем стойността, която трябва да бъде съхранена.

string greeting = "Hello, World!";

Присвояване на стойност от друг низ. При присвояване стойността на един низ на друг новата променлива приема референцията към същото място в динамичната памет, към което сочи първата.

string initial = "Some Text";
string assigned = initial;

Приемане на стойност от операция, която връща string. Това може да бъде метод, който валидира данни; преобразуване на друг тип променлива; събиране на няколко низа.

string firstName = "Georgi";
string lastName = "Georgiev";
string fullName = firstName+lastName;

// Georgi Georgiev

Операции със символни низове

Трябва да се отбележи, че символните низове са неизменими и всяка промяна на един низ води до създаване на нов, в който се пази резултата и пренасочване към него. Това може да силно да забави изпълнението на една програма при много редакции на един низ (например долепване на символи в цикъл). В такива случаи използваме класа String Builder, който оптимизира работата с низове.

Сравняване на низове

Сравняване за еднаквост. Удобен за това е метода Equals(), който работи еквивалентно на '=='. Метода връща булев резултат 'true' при еднакви низове и 'false' при различни. Трябва да се има в предвид, че по този начин се сравняват побуквено елементите на масива, като се прави разлика между главни и малки букви. Често ще ни интересува само текстовото съдържание без регистъра на буквите, затова може да използваме метода 'Equals()' с параметър 'StringComparison.CurrentCultureIgnoreCase'
string word1 = "C#";
string word2 = "c#";
Console.WriteLine(word1.Equals("C#"));
Console.WriteLine(word1.Equals(word2));
Console.WriteLine(word1=="C#");
Console.WriteLine(word1==word2);

// True
// False
// True
// False
Сравняване на низове по азбучен ред. За тази цел се ползва метода 'String.CompareTo()', който връща отрицателна стойност, когато първият низ е лексикографски преди втория, нула при равни стойности и положителна стойност при втори преди първи низ. Отново трябва да се има в предвид, че сравнението взима под внимание регистъра на буквите като малките са преди големите. Ако искаме да сравним двата низа без да взимаме под внимание регистъра ползваме статичния метод 'string.Compare(string strA, string strB, bool ignoreCase) '. Вместо булевата променлива 'ignoreCase' можем да подадем 'StringComparison.CurrentCultureIgnoreCase', с което метода отново ще работи.
string str1 = "sCore";
string str2 = "score";
Console.WriteLine(str1.CompareTo(str2));
Console.WriteLine(str2.CompareTo(str1));
Console.WriteLine(str1.CompareTo(str1));
Console.WriteLine(string.Compare(str1, str2 , true));
Console.WriteLine(string.Compare(str1, str2 , StringComparison.CurrentCultureIgnoreCase));

// 1
// -1
// 0
// 0
// 0

Долепване на низове (конкатенация)
Долепването на символни низове става чрез операторите '+', '+=' и метода 'Concat()'. Към всеки низ може да се добави всеки друг тип, който може да се представи под формата на низ.

string greeting = "Hello, ";
string name = "Pesho!";
string result = string.Concat(greeting, name);
result = result + " How are you?\n";
result += "It's " + DateTime.Now;
Console.WriteLine(result);

//Hello, Pesho! How are you?
//It's 22.8.2013 г. 7:33:31

Промяна на регистъра на символен низ
Понякога се налага да приведем един низ в единен регистър (при записване на потребителски имена в база данни, проверка на парола и т.н.). Това става с двата метода 'ToUpper()' и 'ToLower()'.

string text = "SoMe teXt";
Console.WriteLine(text.ToUpper());
Console.WriteLine(text.ToLower());

//SOME TEXT
//some text

Търсене на низ в друг низ
За търсене в даден низ се ползват двата метода 'IndexOf()' и 'LastIndexOf()', като първия търси от началото на низа към края му, а втория на обртно. И двата метода връщат цяло число когато намерят индекс и '-1' ако няма намерена стойност, като регистъра на текста е от значение. Ако искаме да търсим от определена стойност, можем да подадем параметър с индекса, от който да започва търсенето.

string text = "Some text";
int indexEFirst = text.IndexOf("e");
int indexESecond = text.IndexOf("e",5);
int lastIndex = text.LastIndexOf("e");
Console.WriteLine(indexEFirst);
Console.WriteLine(indexESecond);
Console.WriteLine(lastIndex);

// 3
// 6
// 6

Извличане на част от низ
За тази цел използваме метода 'Substring()' с подадени параметри за начален индекс и дължина. Ако не подадем дължина на подниз се взима от дадения индекс до края на първия низ.

string text = "Some text here";
string extracted = text.Substring(5, 4);
string extractedToEnd = text.Substring(5);
Console.WriteLine(extracted);
Console.WriteLine(extractedToEnd);

// text
// text here

Разцепване на низ по разделител
За да се изведат в масив елементите от списък записан в стринг се ползва метода 'Split()', на който подаваме разделител или масив от разделители. Тъй като при списъците има допиране на разделители, често се получават празни полета в новосъздадения масив, затова в параметрите на метода подаваме 'StringSplitOptions.RemoveEmptyEntries', който да приема два съседни разделителя за един.

string listOfNames = "Gosho, Pesho, Tosho, Ivan";
char[] separators = new char[] {' ',',','.'};
string[] Names = listOfNames.Split(separators,StringSplitOptions.RemoveEmptyEntries);

Премахване на ненужни символи от стринг
За да премахнем празни места (нови редове, интервали и табулации) в началото и в края на един символен низ използваме метода 'Trim()'. При подаване на масив със символи същият метод ги търси и изтрива само тях в символния низ. Ако искаме да премахнем ненужните символи и празни места само в началото или само в края използваме съответно 'TrimStart()' и 'TrimEnd()'

string data1 = " 111 $ % Text Example ### s ";
string data2 = "     \n\n Text Example      ";
char[] trimChars = new char[] { ' ', '1', '$', '%', '#', 's' };
Console.WriteLine(data2.Trim());
Console.WriteLine(data1.Trim(trimChars));
Console.WriteLine(data1.TrimEnd(trimChars));

// Text Example
// Text Example
//  111 $ % Text Example

Оптимизация на паметта при символни низове
Когато инициализираме променлива от тип string с низов литерал, динамичната памет се обхожда и се прави проверка дали такава стойност вече съществува. Ако съществува, новата променлива просто се пренасочва към нея. Ако не, референцията се препраща да сочи към нов блок памет. Интернирането на низове в .NET е възможно, защото низовете по концепция са неизменими и няма опасност някой да промени областта, сочена от няколко стрингови променливи едновременно. Когато не инициализираме низовете с литерали, не се ползва интерниране. Все пак, ако искаме да използваме интерниране изрично, можем да го направим чрез метода 'Intern()'. За да демонстрираме това по-долу използваме статичния метод 'Object.ReferenceEquals()', който проверява дали два обекта сочат към един и същ блок памет.

string declared = "Some text";
string built = new StringBuilder ("Some text").ToString();
string interned = string.Intern(built);
Console.WriteLine(object.ReferenceEquals(declared, built));
Console.WriteLine(object.ReferenceEquals(declared, interned));

// False
// True

String Builder

Източници