Към съдържанието
wannacode

Работа с текст

Проблем

Кодът е верен, а не минава

Магазинът дава 10% отстъпка с код SUMMER10. Клиентът го преписва от рекламата, но пише с малки букви и случайно слага интервал отпред:

const code = await ask("");
if (code === "SUMMER10") {
console.log("Отстъпка 10%");
} else {
console.log("Невалиден код");
}
rl.close();
Вход
summer10
Изход
Невалиден код

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

Идеята

Текстът е поредица от символи

Текстът е подредени един след друг символи: букви, цифри, интервали, знаци. Всеки символ има индекс, като в масив. Първият е с индекс 0.

const code = "SUMMER10";
console.log(code.length);
console.log(code[0]);
console.log(code[code.length - 1]);
Изход
8
S
0
  • code.length е броят на символите.
  • code[0] е символът с индекс 0. Последният символ е с индекс length - 1.

Отделен тип за един символ (в Java и C# се казва char) тук няма. Символът е просто текст с дължина 1. Единичните и двойните кавички правят едно и също: 'S' и "S" са еднакви текстове.

const code = "SUMMER10";
console.log(code.at(-1));
console.log(code[-1]);
console.log(typeof code[0]);
console.log(code[0].length);
console.log('S' === "S");
Изход
0
undefined
string
1
true

code.at(-1) е последният символ: отрицателният индекс брои отзад. С квадратни скоби това не става: code[-1] дава undefined.

И при текста, и при масива дължината е length, без скоби.

Виж го

Символ по символ

Колко цифри има в телефонния номер? Минаваш с цикъл през индексите от 0 до последния и гледаш всеки символ:

const phone = "0888 123 456";
let digits = 0;
for (let i = 0; i < phone.length; i++) {
const c = phone[i];
if (c >= "0" && c <= "9") {
digits++;
}
}
console.log("Цифри: " + digits);
Изход
Цифри: 10

c >= "0" && c <= "9" работи, защото всеки символ има код: число в таблицата със символите. Текстовете се сравняват по кодовете на символите. Цифрите от "0" до "9" имат поредни кодове, буквите от "a" до "z" също.

По-точно, за любопитните
  • Готови методи като „цифра ли е“ няма. За цифра сравняваш, както горе. За буква: символът е главна буква, ако c !== c.toLowerCase() (малката му форма е друга), и малка, ако c !== c.toUpperCase(). Това знае и кирилицата.
  • Кодът на символ дава "A".charCodeAt(0), тоест 65. Обратно, String.fromCharCode(66) е "B". Внимавай: "a" + 1 не е грешка, а текстът "a1".
  • Без индекс: for (const c of phone) минава направо през символите на текста.
const c = "Ж";
console.log(c >= "0" && c <= "9");
console.log(c.toUpperCase() !== c.toLowerCase());
console.log(c !== c.toLowerCase());
console.log("ж" !== "ж".toUpperCase());
console.log("A".charCodeAt(0), String.fromCharCode(66));
console.log("a" + 1);
let digits = 0;
for (const ch of "0888 123 456") {
if (ch >= "0" && ch <= "9") {
digits++;
}
}
console.log(digits);
Изход
false
true
true
true
65 B
a1
10

Пробвай

Кой символ?

Какво ще отпечата програмата?

const word = "Поръчка";
console.log(word[3]);
console.log(word.length);
Отговор

Идеята

Методите на текста

Текстът идва с готови методи: действия, които викаш с точка след текста. Ето поправката на промо кода:

const code = " summer10";
const clean = code.trim().toUpperCase();
console.log("[" + clean + "]");
console.log(clean === "SUMMER10");
Изход
[SUMMER10]
true

trim() маха интервалите отпред и отзад. toUpperCase() прави всички букви главни. Методите се нареждат един след друг: първият връща текст и върху него се вика вторият.

Още няколко, върху имейл адрес:

const email = "ivan.petrov@mail.bg";
console.log(email.includes("@"));
console.log(email.indexOf("@"));
console.log(email.slice(0, email.indexOf("@")));
console.log(email.replaceAll(".", " "));
console.log(email.endsWith(".bg"));
Изход
true
11
ivan.petrov
ivan petrov@mail bg
true
Метод Какво прави
includes(x) има ли го x вътре
indexOf(x) на кой индекс е x за първи път (-1, ако го няма)
slice(a, b) част от текста
replaceAll(x, y) всяко x се заменя с y
startsWith(x), endsWith(x) започва ли (свършва ли) с x
toLowerCase() всички букви малки

Внимавай с replace: в JavaScript той сменя само първото срещане. За всички пишеш replaceAll.

console.log("ivan.petrov@mail.bg".replace(".", " "));
console.log("ivan.petrov@mail.bg".indexOf("#"));
console.log("Кафе".startsWith("К"));
console.log("Кафе".toLowerCase());
Изход
ivan petrov@mail.bg
-1
true
кафе
По-точно, за любопитните

slice(a, b) взима символите от индекс a до индекс b, без b. Броят им е b - a. Само с един аргумент взима всичко до края. Отрицателният индекс брои отзад: slice(-2) са последните два символа.

const date = "2026-10-24";
console.log(date.slice(5, 7));
console.log(date.slice(8));
console.log(date.slice(-2));
Изход
10
24
24

Идеята

Текстът не се променя

Има една особеност, която обърква всички в началото:

let name = "мария";
name.toUpperCase();
console.log(name);
name = name.toUpperCase();
console.log(name);
Изход
мария
МАРИЯ

Вторият ред не прави нищо видимо. toUpperCase() не променя name. Той прави нов текст с главни букви и го връща. Никой не го записва и той се губи.

Веднъж създаден, текстът не може да се промени. Казва се, че е неизменим (immutable). Всеки метод, който „променя“ текст, всъщност връща нов. За да го запазиш, присвояваш: name = name.toUpperCase();.

let name = "мария";
name.toUpperCase();
console.log(name);
name = name.toUpperCase();
console.log(name);
Стъпка по стъпка, с думи
  1. В name е текстът „мария“: 5 символа с индекси от 0 до 4.
  2. toUpperCase() не пипа „мария“. Прави нов текст „МАРИЯ“.
  3. Никой не записва новия текст и той се губи.
  4. В name си е „мария“ и то се отпечатва.
  5. Пак нов текст „МАРИЯ“, но сега = го записва в name. Старото „мария“ отпада.
  6. Сега в name е „МАРИЯ“.
По-точно, за любопитните
  • Символ вътре в текста не може да се смени и с индекс. name[0] = "М" в обикновен скрипт мълчи: няма грешка, но name си остава "мария". Във файл с import (модул) същият ред спира програмата с TypeError: Cannot assign to read only property '0' of string. Правиш нов текст, например "М" + name.slice(1).
  • Когато имаш много парчета, събери ги в масив и ги долепи наведнъж с join:
const parts = ["мляко", "хляб", "яйца"];
console.log(parts.join(", "));
Изход
мляко, хляб, яйца

Пробвай

Намери грешката

Потребителят въвежда имейла си с главни букви и интервали. Програмата трябва да го изчисти и да го пусне, но казва „Няма такъв потребител.“. На кой ред е грешката?

Избери реда с грешката.

Идеята

Текстът на части

Често текстът съдържа няколко неща, разделени със знак: продукти със запетая, думи с интервал. split го разделя на части и връща масив от текстове:

const list = "мляко,хляб,яйца,сирене";
const items = list.split(",");
console.log("Продукти: " + items.length);
for (let i = 0; i < items.length; i++) {
console.log((i + 1) + ". " + items[i]);
}
Изход
Продукти: 4
1. мляко
2. хляб
3. яйца
4. сирене

Разделителят не влиза в частите. Оттук нататък работиш с обикновен масив: дължина, индекси, цикъл.

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

const parts = (await ask("")).split(" ");
let sum = 0;
for (const p of parts) {
sum += Number(p);
}
console.log(sum);
rl.close();
Вход
42 38 51 47 60
Изход
238
По-точно, за любопитните
  • Два интервала един до друг дават празна част '' между тях. split(/ +/) (един или повече интервала) решава това. Нещото между наклонените черти е регулярен израз, шаблон за текст. Иначе split получава обикновен текст и split(".") разделя по точка.
  • Обратното на split е items.join(", "): долепва частите с разделител.
console.log("а б".split(" "));
console.log("а б".split(/ +/));
console.log("а.б".split("."));
console.log(["а", "б"].join(", "));
Изход
[ 'а', '', 'б' ]
[ 'а', 'б' ]
[ 'а', 'б' ]
а, б

Пробвай

Едно и също ли е?

В JavaScript === сравнява съдържанието на текстовете, буква по буква. Но главната и малката буква са различни символи. Въведено е София:

Какво ще отпечата програмата?

const city = await ask("");
console.log(city === "София");
console.log(city === "софия");
console.log(city.toLowerCase() === "софия");
rl.close();
Отговор
По-точно, за любопитните

Има и == с две равенства. Той първо превръща двете страни в един тип и чак тогава сравнява, затова текстът "1" и числото 1 излизат равни:

console.log("1" == 1);
console.log("1" === 1);
Изход
true
false

Сравнявай винаги с ===.

Упражнение

Имена и думи

Реши задачите в редактора си. Текстът се чете от клавиатурата като цял ред.

1. Името наобратно. Прочети име и го отпечатай отзад напред. Минаваш през индексите от последния към 0 и долепваш символите в нов текст.

Вход
Мария
Изход
яираМ

2. Брой думи. Прочети изречение и преброй думите. Между думите може да има повече от един интервал, а в началото и в края може да има интервали. Подсказка: дума започва там, където символът не е интервал, а предният е интервал (или няма преден).

Вход
Днес е хубав ден
Изход
Думи: 4

3. Палиндром. Палиндромът се чете еднакво отпред назад и отзад напред. Интервалите и главните букви не се броят. Махни интервалите, направи буквите малки и сравни първия символ с последния, втория с предпоследния и т.н.

Вход
Аз обичам мач и боза
Изход
Палиндром е
Вход
Кафе с мляко
Изход
Не е палиндром

Упражнение

Проверки и броене

4. Сигурна парола. Питай за парола, докато не въведат сигурна: поне 8 знака, поне една цифра, една главна и една малка буква. След всяка слаба парола кажи какво се иска. Подсказка: do-while и проверка на всеки символ: цифра с c >= "0" && c <= "9", главна с c !== c.toLowerCase(), малка с c !== c.toUpperCase().

Вход
kafe
kafe2026
Kafe2026
Изход
Парола:
Слаба парола: поне 8 знака, цифра, главна и малка буква.
Парола:
Слаба парола: поне 8 знака, цифра, главна и малка буква.
Парола:
Паролата е приета.

Още задачи

  • Запетаи вместо интервали. Прочети изречение и замени всеки интервал със запетая. Утре заминаваме за Варна → Утре,заминаваме,за,Варна.
  • Най-дългото име. Прочети имена, разделени със запетая, и отпечатай най-дългото. Ана,Кристина,Петър → Кристина.
  • Гласните със звездичка. Замени всяка гласна (а, ъ, о, у, е, и) със *, независимо дали е главна или малка. Обичам кафе → *б*ч*м к*ф*.
  • Честота на буквите. Преброй колко пъти се среща всяка буква в текста, без значение главна или малка. Подсказка: масив с 32 брояча, по един за всеки символ от 'а' до 'я' (в таблицата със символите между тях има и две руски букви, ы и э, техните броячи просто остават 0). Индексът на буквата c е c.charCodeAt(0) - "а".charCodeAt(0). Отпечатай само буквите, които се срещат.
  • Графика на символите. Като горната задача, но вместо числото отпечатай толкова звездички: а: ****.
  • Колко пъти всяка дума. Прочети изречение и за всяка различна дума отпечатай колко пъти се среща. Подсказка: раздели на думи и за всяка дума провери дали вече не е броена по-рано в масива.
  • Сортиране по дължина. Прочети думи, разделени с интервал, и ги отпечатай от най-късата към най-дългата. Сортирай масива както сортираше числа, но сравнявай дължините.
  • Брой изречения. Преброй изреченията в текст: всяко свършва с ., ! или ?. Внимавай с ... и ?!: те завършват едно изречение, не три.
  • Балансирани скоби. Провери дали скобите в текста са балансирани. С един вид скоби стига брояч: ( добавя 1, ) вади 1. Броячът не бива да пада под 0 и накрая трябва да е 0. С три вида, (), [], {}, ти трябва масив от символи, в който пазиш отворените скоби, и брояч докъде е пълен: при затваряща скоба последната отворена трябва да е от същия вид. {[()]} е балансиран, ([)] не е.

Тест

Провери се

1. Какво дава "Кафе".length?
2. Какво прави редът name.toUpperCase(); сам, без присвояване?
3. Какво дава "Варна" === "варна" в JavaScript?
4. Каква е разликата между 'а' и "а" в JavaScript?
5. Какво дава "Пн,Ср,Пт".split(",")?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·