Садржај
1 Алгоритми
2 Како се програмира
3 Променљиве, подаци, типови
3.1 Реални бројеви
3.2 Целобројни типови
3.3 Конверзије и заокруживање
3.4 Знаковни подаци
3.5 Текстуални подаци (стрингови, ниске)
3.6 Логички подаци
4 Гранања
4.1 Наредба if
4.2 Остали облици if наредбе
4.3 Сложени услови
4.4 Неке типичне употребе if наредби
4.5 Угнежђено гранање
4.6 Наредба switch
4.7 Гранања - разни задаци
5 Петље
5.1 Врсте петљи
5.2 Наредбе break и continue
5.3 Основни алоритми са петљама
5.4 Цифре броја
5.5 Дељивост
5.6 Угнежђене петље
5.7 Петље - разни задаци
6 Статички методи
6.1 Писање статичких метода
6.2 Извршавање метода
6.3 Пренос аргумената по референци
6.4 Корист од метода
7 Низови
7.1 Употреба низова
7.2 Низови - вежбање
7.3 Низ као референцирани тип
7.4 Листе
7.5 Стринг и низ карактера
8 Матрице
8.1 Употреба матрица
9 Кориснички дефинисани типови
9.1 Набројиви типови и структуре
10 Фајлови
10.1 Управљање фајловима
10.2 Читање и упис података у текстуални фајл
10.3 Аргументи командне линије програма
Стринг и низ карактера¶
У приручнику Увод у програмирање у програмском језику C# прочитајте поглавље 7.6 (стране 197-206).
Стринг је специфичан тип података, који по својим особинама донекле личи, али се и разликује од свих осталих типова.
Стрингове смо до сада користили скоро искључиво као атомичне (целовите, нерастављиве) податке, то јест исто као што користимо целобројне или реалне променљиве. На пример, уобичајени начин употребе целих бројева изгледа овако:
int a = 4, b = 5;
int c = a + b;
Console.WriteLine(c);
а на исти начин је могуће користити и стрингове:
string a = "programski ", b = "jezik";
string c = a + b;
Console.WriteLine(c);
Сада ћемо упознати особине стрингова по којима они много више личе на низове.
Приступ појединим карактерима стринга¶
У многим случајевима стринг можемо да користимо на исти начин као и низ. На пример, без обзира на то да ли променљиву s
декларишемо овако:
char[] s = { 'Z', 'd', 'r', 'a', 'v', 'o' };
или овако:
string s = "Zdravo";
у наставку програма можемо да пишемо
Console.WriteLine("Broj karaktera u datoj kolekciji je {0}.", s.Length);
Console.Write("Ti karakteri su: " + s[0]);
for(int i = 1; i < s.Length; i++)
Console.Write(", " + s[i]);
Console.WriteLine(".");
int n = 0;
foreach (char c in s)
if (c == 'a') n++;
Console.WriteLine("Slovo 'a' se u kolekciji pojavljuje {0} puta.", n);
и у оба случаја добијамо резултат
Broj karaktera u datoj kolekciji je 6.
Ti karakteri su: Z, d, r, a, v, o.
Slovo 'a' se u kolekciji pojavljuje 1 puta.
Неизменљивост стринга¶
Најважнија разлика између стринга и низа карактера је у томе што елементи стринга могу само да се читају, а не и да се мењају. Због тога би наредба
s[0] = 'z';
у случају низа карактера s
била нормално извршена, док у случају стринга s
иста „наредба” у ствари представља синтаксну грешку. Дакле, сам језик C# не допушта могућност мењања појединих елемената или делова стринга, онако како то радимо са низом. Разлози за ово ограничење су повезани са начином на који се кодирају Unicode карактери, то јест са чињеницом да кодови појединих карактера не морају да буду исте дужине.
Ову особину стрингова зовемо неизменљивост, или имутабилност (енгл. immutable - неизменљив). Стрингу се (наравно) може додељивати нова вредност, што смо и до сада чинили, али то није у сукобу са тврдњом да је једном формиран стринг неизменљив. Додељивање вредности стрингу значи формирање новог стринга, а не преправљање постојећег, мада то није увек очигледно. На пример, ако за неки стринг recenica
напишемо
recenica += '!';
то је само поједностављен начин писања (такозвани синтаксни шећер ) пуног облика наредбе
recenica = recenica + '!';
Ово су два записа исте наредбе (и значење и начин извршавања су потпуно исти), а из пуног облика се јасније види да се овом наредбом мења стринг као целина.
Погледајмо поново пример игре погађања речи из Приручника (стр. 198-199). Када играч који погађа реч унесе слово, то слово се пореди са свим словима задате речи. Пошто се слова запамћена у задатој речи не мењају током рада програма, ову реч можемо да чувамо као стринг (променљива rec
у програму). Са друге стране, карактери полу-погођене речи се потенцијално ажурирају након сваког покушаја погађања. Због тога полу-погођену реч не можемо да чувамо у стрингу већ за њу користимо низ карактера skrivenaRec
.
Формирање новог стринга у принципу значи алоцирање новог простора и преписивање карактера у тај нови простор (интелигентна имплементација стрингова само у изузетним случајевима успева да избегне ове споре операције). Зато треба избегавати често формирање нових стрингова, поготово ако су они веома дугачки. Следећа лекција се бави различитим начинима трансформисања стрингова без сувише честог формирања нових стрингова.
Стринг као референцирани тип¶
Стринг је, као и низ, референцирани тип података. Због тога, поред могућности индексирања, стрингови имају још неке заједничке особине са низовима:
простор за садржај стринга се заузима у динамичкој меморији
додељивање стринга стрингу попут
s1 = s2;
је додељивање референце (након доделеs1
иs2
користе исту меморију)када је стринг аргумент метода, он се преноси као референца (без копирања садржаја), па позвани метод преко добијене референце приступа истој меморији као и позивајући метод
Када током одређене употребе неког стринга немамо потребе да га мењамо, методи су ефективан начин да се та употреба стринга издвоји у засебну целину.
Пример - испис стања у игри судоку
Тренутно стање у игри судоку представљено је стрингом од 81 карактера. То стање се може приказати у облику табеле, као што је учињено у методу IspisiTablu
у следећем програму.
Исписана табела изгледа овако:
+---+---+---+
|.71|..4|...|
|...|298|1..|
|.2.|..7|..3|
+---+---+---+
|64.|8..|379|
|.1.|...|.4.|
|789|..5|.12|
+---+---+---+
|4..|9..|.3.|
|..7|416|...|
|...|7..|86.|
+---+---+---+
Погледајмо сада и неке особине стринга, по којима се он као референцирани тип разликује од низа.
Мада су и низ и стринг референцирани типови, оператор ==
није изведен на исти начин за низове и стрингове. Као што знамо, код низова ==
значи поређење референци. Код стрингова су творци језика C# узели у обзир двојаку природу стрингова и одлучили да ==
за стрингове значи поређење садржаја. Ово има смисла не само зато што стрингове доживљавамо и као целине (док су низови пре свега колекције), него и зато што због неизменљивости стрингова није ни битно да ли су два стринга са једнаким садржајем исти или различити као референце.
Постоји и једна, више субјективна разлика између стринга и низа као референцираних типова, мада формалне разлике у том делу нема. Као што знамо, низ често модификујемо тако што мењамо вредности његових елемената, а стринг мењамо искључиво формирањем целог новог стринга. Због тога различито доживљавамо могућности над низом и стрингом, који су аргументи метода.
На следећем примеру ћемо објаснити о чему је реч:
Програм приликом извршавања исписује само Zdravo
(без узвичника). То се догађа зато што се из метода DodajUzvicnik
формира нови стринг и аргумент str
се преусмерава на меморијску локацију која садржи тај нови стринг. Тиме се не утиче на оригинални стринг s
, који из позивајућег метода и даље референцира оригинални стринг.
Да би метод DodajUzvicnik
могао да промени вредност стринга, потребно је или користити реч ref
испред аргумента:
static void DodajUzvicnik(ref string str) { str += '!'; }
// ...
DodajUzvicnik(ref s);
или написати метод коме је стринг повратна вредност:
static string DodajUzvicnik(string str) { return str + '!'; }
// ...
s = DodajUzvicnik(s);
Иако оваква употреба стрингова делује другачије од онога што радимо са низовима, аналогија у понашању референци је потпуна. Прва верзија метода
// ovo ne radi
static void DodajUzvicnik(string str) { str += '!'; }
не функционише из истог разлога, из којег не функционише ни метод
// ni ovo ne radi
static void NapraviNoviNiz(int[] a) { a = new int[10]; }
То што низ a
(тј. његове елементе) можемо да модификујемо у методу NapraviNoviNiz
, а стринг str
не можемо у методу DodajUzvicnik
је последица неизменљивости стрингова, а не неке разлике у понашању референци (разлике у понашању референци нема). Када хоћемо да низу из метода доделимо нову меморију, потребно је да користимо реч ref
или out
испред аргумента или да низ буде повратна вредност метода, баш као и са стринговима:
static void NapraviNoviNiz(ref int[] a) { a = new int[10]; }
// ...
NapraviNoviNiz(ref a);
или
static int[] NapraviNoviNiz() { return new int[10]; }
// ...
a = NapraviNoviNiz();
Приметимо да је (као и код низова) коришћење речи ref
и out
код аргумената типа стринг у пракси ретко и неуобичајено. Уместо тога, врло је честа пракса да стринг формиран у методу буде повратна вредност тог метода. Подсетимо се метода Substring
, Insert
, Remove
, PadLeft
, PadRight
, ToLower
, ToUpper
, Trim
, TrimEnd
, TrimStart
који служе за различита трансформисања стринга. Ниједан од њих не мења оригинал, а свима им се трансформисани стринг јавља као повратна вредност. Када желимо да трансформишемо оригинални стринг користећи неки од ових метода, пишемо наредбу облика s = s.f(...)
, где је f
дати метод (уместо тачкица треба навести одговарајуће аргументе).