Назад Зміст Вперед

Розкладання рядків на лексеми. Форматоване виведення даних

Розкладання рядків на лексеми. Форматоване виведення даних 

Повторення mutable/immutable

Об'єкти, які після створення можна змінити, називаються змінними або mutable.
Об'єкти, які після їх створення змінити не можна, називаються незмінними або immutable.
У незмінних об'єктів багато корисних властивостей. Але можна виділити дві, які характерні практично для всіх immutable об'єктів:
1. Незмінні об'єкти можна реалізувати значно простіше, ніж змінювані.
2. Незмінні об'єкти можна вільно використовувати одночасно з різних потоків.
Клас String - це immutable клас. Всі об'єкти типу String - незмінні, що, однак, не заважає нами з ними працювати.
Наприклад, метод toUpperCase () класу String перетворює рядок у верхній регістр (замінює все маленькі букви на великі). Але цей метод не змінює сам рядок, а повертає новий рядок, який ідентичний першому, тільки всі символи в верхньому регістрі (великі). Клас Integer - це теж immutable клас. Всі об'єкти типу Integer - незмінні. Кожен раз, коли ми змінюємо об'єкт Integer - насправді створюється новий об'єкт.

Всі класи, пов'язані з рядками, їх конструктори та методи

Важливе місце в обробці інформації займає робота з текстами. Як і багато чого, текстові рядки в мові Java є об'єктами. Вони представляються екземплярами класу String або класу StringBuffer
У класі String 46 методів. Розробники Java помітили, що в більшості рядки всередині програм існують в незмінному вигляді, але іноді їх все-таки змінюють. Дуже неприємно, коли ви створили рядок, зберегли в нього щось важливе, передали в інший метод, а він його поміняв. Щоб такого не відбувалося, розробники вирішили створити два види рядків - зміні (mutable) і незмінні (imutable).
Клас String став відповідати за незмінний рядок, а клас StringBuilder - за змінний. Об'єкти цих класів легко перетворюються один в одного. Тобто, якщо потрібен рядок - треба створити клас String, а якщо потрібен змінний рядок - то клас StringBuilder. Але насправді є ще клас StringBuffer - це копія StringBilder, але всі методи якого оголошені synchronized, для того, щоб до об'єкта можна було звертатися з різних потоків програми.
В об'єктах класу String зберігаються рядки-константи незмінної довжини і змісту. Це значно прискорює обробку рядків і дозволяє економити пам'ять, розділяючи рядок між об'єктами, які його використовують. Довжину рядків, що зберігаються в об'єктах класу StringBuffer, можна змінювати, вставляючи і додаючи рядки і символи, видаляючи підрядки або зчіплючи декілька рядків у один рядок. В багатьох випадках, коли треба змінити довжину рядка типу String, компілятор Java неявно перетворює його до типу StringBuffer, змінює довжину, потім перетворює назад в тип String.
Наприклад:

Компілятор виконає так:
String s = "Це" + " один " + "рядок";

String s = new tringBuffer().append ("Це").append(" один ").append ("рядок").toString();

Буде створений об'єкт класу StringBuffer, в нього послідовно додані рядки "Це", " один ", "рядок", і одержаний об'єкт класу StringBuffer буде приведений до типу String методом toString (). Нагадаємо, що символи в рядках зберігаються в Unicode, в якій кожен символ займає два байти, а тип кожного символу - char.

Клас String
Перед роботою з рядком його треба створити. Це можна зробити різними способами.
Створення рядка:
·  Найпростіший спосіб - організувати посилання типу String на рядок-константу:
String s1= "Це рядок.";
Якщо константа довга, можна записати її в декількох рядках текстового редактору, зв'язуючи їх операцією зчеплення:
String s2 = "Це довгий рядок, " + "записаний в двох рядках вихідного тексту";
!!! String s = "", - не містить жодного символу,
String s = null - вказує ні на один рядок і не являється об'єктом.
· Найправильніший спосіб створити об'єкт з точки зору ООП — це викликати його конструктор в операції new. Клас String має дев'ять конструкторів:
1.  String() — створюється об'єкт з пустим рядком;
2. String (String str) — із одного об'єкта створюється інший, тому цей конструктор використовується рідко;
3.  String (StringBuffer str) — перетворена копія об'єкту класу BufferString;
4.  String(byte[] byteArray) — об'єкт створюється із масиву байтів byteArray;
5. String (char [] charArray) — об'єкт створюється із масиву charArray символів Unicode;
6. String (byte [] byteArray, int offset, int count) — об'єкт створюється із частини масиву байтів byteArray, починається з індексу offset і містить count байтів;
7. String (char [] charArray, int offset, int count) — те ж саме, але масив складається із символів Unicode;
8. String(byte[] byteArray, String encoding) — символи, записані в масиві байтів, задаються в Unicode-рядку з врахування кодування encoding;
9.  String(byte[] byteArray, int offset, int count, String encoding) — те ж саме, але тільки для частини масиву.

При неправильному заданні індексів offsetcount або кодування encoding виникає виключна ситуація. 
· Ще один спосіб створити рядок — це використати два статичні методи
1. copyValueOf(chart] charArray)
2. copyValueOf(char[] charArray, int offset, int length).
Вони створюють рядок по заданому масиву символів і повертають його в якості результату своєї роботи. Наприклад, після виконання наступного фрагменту програми
char[] с = ('С', 'и', 'м', 'в', ' о', 'л', 'ь', 'н', 'и', 'й'};
String s1 = String.copyValueOf(с);
String s2 = String.copyValueOf(с, 3, 7);
одержимо в об'єкті s1 рядок "Символьний", а в об'єкті s2 - рядок "вольний".

Зчеплення рядків (concatenation +)
З рядками можна проводити операцію зчеплення рядків (concatenation), що позначається знаком плюс+. Ця операція створює новий рядок, просто складений із першого і другого рядка. Її можна застосувати і до констант, і до змінних.
Наприклад:
String attention = "Увага: ";
String s = attention + "невідомий символ";

Друга операція - присвоювання += застосовується до змінних в лівій частині:
attention += s;
Оскільки операція + перевантажена з додавання чисел на зчеплення рядків, постає питання про пріоритет цих операцій. У зчепленні рядків пріоритет вище, ніж у додавання, тому, записавши "2" + 2 + 2, одержимо рядок "222". Але, записавши 2 + 2 + "2", одержимо рядок "42", оскільки дії виконуються зліва направо. Якщо ж запишемо "2" + (2 + 2), то одержимо "24".

Методи для роботи з рядками

В класі String є багато методів для роботи з рядками:

1. Довжина рядка
Для того щоб визначити довжину рядка, тобто кількість символів в ній, треба звернутися до методу length():
Метод
Приклад
int length();
String s = "Good news everyone!";
int n = s.length();
int n = "Good news everyone!".length();

2. Як вибрати символи із рядка
Метод charAt повертає символ рядка за його номером. Нумерація символів починається із 0.
Метод
Приклад
char charAt(int index)
String s = "Good news everyone!";
char n = s.сharAt(5);
char n = "Good news everyone!".сharAt(5);
 Метод toCharArray повертає масив всіх символів рядка.
Метод
Приклад
char[]toCharArray ()
String s = "Good news everyone!";
for(char c: s.toCharArray())
{
System.out.println(c);
}

3. Як вибрати підрядок
Метод substring(int begin, int end) виділяє підрядок від символу з індексом begin включно до символу з індексом end виключно. Довжина підрядка буде рівною end - begin.
Метод substring (int begin) виділяє підрядок від індексу begin включно до кінця рядка.
Якщо індекси від'ємні, індекс end більше довжини рядка або begin більше ніж end, то виникає виключна ситуація.
Метод
Приклад
String substring(int beginIndex, int endIndex)
String s = "Good news everyone!";
s = s.substring(1,6);
Результат:
s == "ood n";
String substring(int beginIndex)
String s = "Good news everyone!";
s = s.substring(1);
Коли ми створюємо підрядок за допомогою методу substring, то створюється новий об'єкт String. Але замість того, щоб зберігати посилання на масив з новим набором символів, цей об'єкт зберігає посилання на старий масив символів і разом з цим зберігає дві змінні, за допомогою яких визначає - яка частина оригінального масиву символів відноситься до нього.
Наприклад:
Отримання підрядка
Що зберігається всередині підрядка

String s = "mama";
Що зберігається в s:
char[] value = {'m','a','m','a'};
offset = 0;
count = 4;
Всі три рядки зберігають посилання на один і той же масив char, просто крім цього вони ще зберігають номер першого і останнього символу цього масиву, який відноситься безпосередньо до їх об'єкту. Вірніше, номер першого символу і кількість. 
Тому, якщо взяти рядок довжиною 10,000 символів і зробити з нього 10,000 підрядків будь-якої довжини, то ці «підрядки» будуть займати дуже мало пам'яті, тому що масив символів не дубльований. Рядки, які повинні займати купу місця, будуть займати буквально пару байт.

Strins s2 = s.substring(1);
Що зберігається в s2:
char[] value = {'m','a','m','a'};
offset = 1;
count = 3;
Strins s3 = s.substring(1, 3);
Що зберігається в s3:
char[] value = {'m','a','m','a'};
offset = 1;
count = 2;

4. Як порівняти рядки
Операція порівняння == співставляє лише посилання на рядки. Вона дивиться, чи вказують посилання на один і той же рядок. Наприклад, для рядків
String s1 = "Якийсь рядок";
String s2 = "Інший рядок";
порівняння s1 == s2 дає в результаті false. Значення true одержимо лише у випадку, коли обидві посилки вказують на один і той же рядок, наприклад, після присвоєння s1 = s2.
Логічний метод equals (object obj), перевизначений із класу Оbject, повертає true, якщо аргумент objне рівний null, являється об'єктом класу String, і рядок, що міститься в ньому, повністю ідентичний даному рядку аж до співпадіння регістру літер. В решті випадків повертається значення false.
Логічний метод equalsIgnoreCase(object obj) працює так само, але однакові літери, записані в різних регістрах, вважаються співпадаючими.
Наприклад, s2.equals("інший рядок") дасть в результаті false, а s2.equalsIgnoreCase("інший рядок") поверне true.
Метод
Приклад
boolean equals (Object o)
String s = "cat";
boolean test1 = s.equals("cat");//true
boolean test2 = s.equals("Cat");//false
boolean test3 = s.equals("c"+"a"+"t");//true
boolean equalsIgnoreCase (String str)
String s = "cat";
boolean test1 = s.equalsIgnoreCase("cat");//true
boolean test2 = s.equalsIgnoreCase("Cat");//true
boolean test3 = s.equalsIgnoreCase("cAT");//true
Метод compareTo(string str) повертає ціле число типу int, обчислене за наступними правилами:
1. Порівнюються символи даного рядка this і рядка str з однаковими індексами, доки не зустрінуться різні символи з індексом, допустимо k, або доки один з рядків не закінчиться.
2. В першому випадку повертається значення this.charAt(k) - str.charAt(k), тобто різниця кодів Unicode перших неспівпадаючих символів.
3. В другому випадку повертається значення this.length() - str.length(), тобто різниця довжин рядків.
4.  Якщо рядки співпадають, повертається 0.
Якщо значення str рівно null, виникає виключна ситуація. Нуль повертається в тій же ситуації, в якій метод equals() повертає true.
Метод compareToІgnoreCase(string str) робить порівняння без врахування регістру літер, точніше кажучи, виконується метод
this.toUpperCase().toLowerCase().compareTo(str.toUpperCase().toLowerCase());
Ще один метод - compareTo(Object obj) створює виключну ситуацію, якщо obj не являється рядком. В решті випадків він працює як метод compareTo(String str). Ці методи не враховують алфавітний порядок символів в локальнму кодуванні.
Порівняти підрядок даного рядка this з підрядком тієї ж довжини len іншого рядка str можна логічним методом regionMatches(int indl, String str, int ind2, int len)
Тут ind1 — індекс початку підрядка даного рядка thisind2 - індекс початку підрядка іншого рядка str.
Результат false отримаємо в наступних випадках:
• хоч би один із індексів ind1 або ind2 від'ємний;
• хоч би одно із ind1 + len або ind2 + len більше довжини відповідного рядка;
• хоч би одна пара символів не співпадає.
Цей метод розрізняє символи, записані в різних регістрах. Якщо треба порівняти підрядки без врахування регістрів літер, то використовуйте логічний метод:
regionMatches(boolean flag, int indl, String str, int ind2, int len)
Якщо перший параметр flag рівний true, то регістр літер при порівнянні підрядків не враховується, якщо false - враховується.

5. Як знайти символ в рядку
Пошук завжди ведеться з врахуванням регістру літер. Першу поява символу ch в даному рядку this можна виявити методом indexOf(int ch), що повертає індекс цього символу в рядку або -1, якщо символу ch в рядку this немає.
Наприклад, "Молоко". indexOf('о') видасть в результаті 1.
Другу і наступні появи символу ch в даному рядку this можна відслідкувати методом indexOf(int ch, int ind).
Цей метод починає пошук символу ch з індекса ind. Якщо ind < 0, то пошук іде з початку рядка, якщо ind більше за довжину рядка, то символ не шукається, тобто повертається -1.
Наприклад, "молоко".indexof('о', indexof ('о') + 1) дасть в результаті 3.
Остання поява символу ch в даному рядку this відслідковується методом 
lastIndexОf (int ch)
Він проглядає рядок в зворотному порядку. Якщо символ ch не знайдено, повертається -1. Наприклад, "Молоко".lastІndexОf('о') дасть в результаті 5.
Передостанню і попередню появу символу ch в даному рядку this можна відслідкувати методом lastIndexОf(int ch, int ind), який проглядає рядок у зворотному порядку, починаючи з індексу ind. Якщо ind більше за довжину рядка, то пошук іде від кінця рядка, якщо ind < 0, то повертається -1.

6. Як знайти підрядок
Методи indexOflastIndexOf дозволяють шукати рядки в рядках. Є 4 види таких методів.
Метод
Приклад
int indexOf(String str)
String s = "Good news everyone!";
int index = s.indexOf ("ne");
Результат:
index == 5
int indexOf(String str, int from)
String s = "Good news everyone!";
int index = s.indexOf ("ne",7);
Результат:
index == 16
та
Метод
Приклад
int lastIndexOf(String str)
String s = "Good news everyone!";
int index = s.lastIndexOf("ne");
Результат:
index == 16
int lastIndexOf(String str,int from)
String s = "Good news everyone!";
int index = s.lastIndexOf("ne",15);
Результат:
index == 5
Пошук завжди ведеться з врахуванням регістру літер. Перше входження підрядка sub в даний рядок this відшукується методом indexОf (String sub). Він повертає індекс першого символу першого входження підрядка sub в рядок або -1, якщо підрядок sub не входить в рядок this. Наприклад, " Молоко".indexof ("ок") дасть в результаті 3.
Якщо ви хочете почати пошук не з початку рядка, а з якогось індексу ind, використовуйте метод indexOf(String sub, int ind.Якщо ind < 0, то пошук іде з початку рядка, якщо ж ind більше довжини рядка, то  символ не шукається, тобто повертається -1.
Останнє входження підрядка sub в даний рядок this можна відшукати методом  lastІndexОf (string sub), що повертає індекс першого символу останнього входження підрядка sub в рядок this або -1, якщо підрядок sub не входить в рядок this.
Останнє входження підрядка sub не у весь рядок this, а тільки в його початок до індексу ind можна відшукати методом lastIndexof(String stf, int ind)
Якщо ind більше довжини рядка, то пошук іде від кінця рядка, якщо ind < 0, то повертається -1.
Для того щоб перевірити, чи не починається даний рядок this з підрядка sub, використовуйте логічний метод startsWith(string sub), який повертає true, якщо даний рядок this починається з підрядка sub, або співпадає з ним, або підрядок sub пустий. 
Можна перевірити і появу підрядка sub в даному рядку this, починаючи з деякого індексу ind логічним методом startsWith(String sub),int ind). Якщо індекс ind від'ємний або більший за довжину рядка, повертається false.
Для того щоб перевірити, чи не закінчується даний рядок this підрядком sub, використовуйте логічний метод endsWitht(String sub). Він повертає true, якщо підрядок sub співпадає з усім рядком або підрядок sub пустий.
Наприклад, if (fileName.endsWith(".Java")) відслідкує імена файлів з вихідними текстами Java.
Перераховані вище методи створюють виключну ситуацію, якщо sub == null. Якщо ви хочете здійснити пошук, який не враховує регістр літер, поміняйте перед цим регістр всіх символів рядка.

7. Як змінити регістр літер
Метод toLowerCase () повертає новий рядок, в якому всі літери переведені в нижній регістр, тобто зроблені прописними.
Метод toUpperCase () повертає новий рядок, в якому всі літери переведені у верхній регістр, тобто зроблені прописними.
Метод
Приклад
String toUpperCase()
String s = "Good news everyone!";
s = s.toUpperCase();
Результат:
s == "GOOD NEWS EVERYONE!";
String toLowerCase()
String s = "Good news everyone!";
s = s.toLowerCase();
Результат:
s == "good news everyone!";

8. Як замінити окремий символ
Метод replace (int old, int new) повертає новий рядок, в якому всі входження символу old замінені символом new. Якщо символу old в рядку немає, то повертається посилання на вихідний рядок. Регістр літер при заміні враховується.

9. Як замінити частину рядка на іншу
Для цього є три методи.
Метод replace замінює всі входження певного символу на інший.
Метод replaceAll замінює всі входження однієї підрядка на іншу.
Метод replaceFirst замінює перше входження переданої підрядка на задану підрядок.
Метод
Приклад
String replace(char oldChar, char newChar)
String s = "Good news everyone!";
String s2 = s.replace>('o', 'a');
Результат:
s2 == "Gaad news everyane!";
String replaceAll(String regex, String replacement)
String s = "Good news everyone!";
String s2 = s.replaceAll ("ne","_");
Результат:
s2 == "Good _ws everyo_!";
String replaceFirst(String regex, String replacement)
String s = "Good news everyone!";
String s2 = s.replaceFirst ("ne","_");
Результат:
s2 == "Good _ws everyone!";

10. Як видалити пробіли на початку і в кінці рядка
Метод trim() повертає новий рядок, в якому видалені початкові і кінцеві символи з кодами, що не перевищують '\u0020'.
Метод
Приклад
String trim()
String s = "Good news everyone!";
s = s.trim();
Результат:
s == "Good news everyone!";

11. Як перетворити дані іншого типу в рядок
В мові Java прийнято, що кожний клас відповідає за перетворення інших типів в тип цього класу і повинен містить потрібні для цього методи. Клас String містить вісім статичних методів valueОf (type elem)перетворення в рядок примітивних типів booleancharintlong, float, double, масиву char[], і просто об'єкту типу Оbject. Дев'ятий метод valueОf(char[] ch, int offset, int len) перетворює в рядок підмасив масиву ch, який починається з індексу offset і має len елементів.
Крім того, в кожному класі є метод toString (), перевизначений або просто унаслідуваний від класу Object. Він перетворює об'єкти класу в рядок. Фактично, метод valueOf() викликає метод toString () відповідного класу. Тому результат перетворення залежить від того, як реалізований метод toString ().
·  Ще один простий спосіб - зчепити значення elem якогось типу з пустим рядком: "" + elem. При цьому неявно викликається метод elem. toString ().

.