Строки. Создание объектов String. Работа со строками.

15.11.2022 14:07

    В JavaScript любые текстовые данные являются строками. Не существует отдельного типа «символ», который есть в ряде других языков. Внутренний формат для строк — всегда UTF-16, вне зависимости от кодировки страницы.

Мы уже не раз использовали строковые литералы, которые представляют собой последовательность символов, заключенную в одинарные или двойные кавычки. Все строки являются объектами типа String и с ними можно производить некоторые действия, например, мы уже знакомы с операцией конкатенации, т.е. объединения строк.

    Для начала определимся с некоторыми понятиями:

  • Алфавит - конечное множество символов.
  • Строка - конечная последовательность символов некоторого алфавита.
  • Пустая строка - строка, не содержащая ни одного символа.

    В JavaScript есть разные типы кавычек.

    Строку можно создать с помощью одинарных, двойных либо обратных кавычек:

 
let single = 'single-quoted';
let double = "double-quoted";
 
let backticks = `backticks`;

    Одинарные и двойные кавычки работают, по сути, одинаково, а если использовать обратные кавычки, то в такую строку мы сможем вставлять произвольные выражения, обернув их в ${…}:

 
function sum(a, b) {
  return a + b;
}
 
alert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3.

    Ещё одно преимущество обратных кавычек — они могут занимать более одной строки, вот так:

 
let guestList = `Guests:
 * John
 * Pete
 * Mary
`;
 

    Содержимое строки в JavaScript нельзя изменить. Нельзя взять символ посередине и заменить его. Как только строка создана — она такая навсегда.

    Давайте попробуем так сделать, и убедимся, что это не работает:

 
let str = 'Hi';
 
str[0] = 'h'; // ошибка
alert( str[0] ); // не работает

    Можно создать новую строку и записать её в ту же самую переменную вместо старой.

Например:
 
let str = 'Hi';
 
str = 'h' + str[1]; // заменяем строку
 
alert( str ); // hi

    Чтобы создать строковый объект можно воспользоваться конструктором newString. Например:

 
var s = newString("Итого:");
 

    Если применить единственное свойство объекта String - length, то мы узнаем длину строки. Для нашего примера, результатом выполнения s.length будет число 6.

 
Рассмотрим методы, влияющие на строку:
  • charAt(n) - возвращает символ, позицию которого определяет параметр n.
  • Для нашего примера, результатом выполнения s.charAt(0) будет буква "И".
  • substr(n1,n2) - возвращает подстроку из строки, где n1 - позиция первого символа подстроки, а n2 - количество символов в строке.

    Для нашего примера, результатом выполнения s.substr(0, 4) будет подстрока "Итог".

    Для примера напишем сценарий, который будет определять, сколько раз заданное слово встречается в определенном тексте.
 
Код html-страницы будет следующим:
 
<html>
  <head>
    <title>javascript строки</title>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script type="text/javascript" src="script.js"></script>
  </head>
  <body>
   <form name="forma11">
     Введите текст:<br>
     <textarea name="textin" rows="4" cols="20"></textarea><hr>
     Введите слово: <input type="text" size="10" name="slovo"><hr>
     <input type="button" value="Определить" onClick="numword(forma11);">
     <input type="reset" value="Отменить"><hr>
     Количество слов в тексте: <input type="text" size="10" name="res">
   </form>
  </body>
</html>
 
Теперь напишем саму функцию numword() на странице script.js:
 
function numword(obj) {
  var t=obj.textin.value;
  var s=obj.slovo.value;
  var m=s.length;
  var res=0;
  var i=0;
  while (i < t.length-1)
  {var ch=t.substr(i,m)
   if (ch==s){
   res+=1;
   i=i+m
   }
   else
   i++
  }
  obj.res.value=res
}
 
Посмотрим на результат, а затем разберем написанное:

    Итак, посмотрим что же мы написали в нашей функции.

    Сначала мы определили две строковые переменные, одной является текст, введенный пользователем, другой - слово.

 
  var t=obj.textin.value;
  var s=obj.slovo.value;
 

    Затем мы определили длину искомого слова и обнулили переменную с результатом:

 
  var m=s.length;
  var res=0;
 

    Далее с помощью цикла while, мы указали следующее: пока параметр цикла i меньше либо равен длине текста (один вычитаем, т.к. нумерация символов начинается с нуля), брать подстроку, начиная с текущего символа i и длинной равной длине искомого слова и сравнивать ее с самим искомым словом. Если результат истинен, то переменная res увеличивается на единицу, а цикл продолжает свою работу с символа, который следует за найденным словом (i=i+m-1). В противном случае - со следующего символа:

 
  var i=0;
  while (i<=t.length-1)
  {var ch=t.substr(i,m)
   if (ch==s){
   res+=1;
   i=i+m-1
   }
   else
   i++
  }
 

Наконец, мы отображаем результат:

 
  obj.res.value=res
 
    В языке javascript определен ряд стандартных функций работы со строками:
 
  • Number(s) - преобразует строковый параметр s в число.
  • String(n) - преобразует число n в строку.
  • isNaN(s) - проверяет является ли параметр s числом. Если параметр s не является числом, то возвращает значение true (истина), в противном случае - false (ложь).

    Помните, когда в предыдущих лекциях, мы брали числа, введенные пользователем, и умножали на 1. Таким образом, мы их из строки переводили в число. Например:

 
  var a1=1*obj.a1.value;
 
    То же самое мы могли бы записать, используя стандартную функцию Number(s):
 
  var a=obj.a1.value;
  var a1=Number(a);
 
    
    Методы toLowerCase() и toUpperCase() меняют регистр символов:
 
alert( 'Interface'.toUpperCase() ); // INTERFACE
alert( 'Interface'.toLowerCase() ); // interface
 
    Если мы захотим перевести в нижний регистр какой-то конкретный символ:
 
alert( 'Interface'[0].toLowerCase() ); // 'i'
 
 
    Существует несколько способов поиска подстроки.
 
    Первый метод — str.indexOf(substr, pos).
 

    Он ищет подстроку substr в строке str, начиная с позиции pos, и возвращает позицию, на которой располагается совпадение, либо -1 при отсутствии совпадений.

Например:

 
let str = 'Widget with id';
 
alert( str.indexOf('Widget') ); // 0, потому что подстрока 'Widget' найдена в начале
alert( str.indexOf('widget') ); // -1, совпадений нет, поиск чувствителен к регистру
 
alert( str.indexOf("id") ); // 1, подстрока "id" найдена на позиции 1 (..idget with id)
Необязательный второй аргумент позволяет начать поиск с определённой позиции.
 
Например, первое вхождение "id" — на позиции 1. Для того, чтобы найти следующее, начнём поиск с позиции 2:
 
let str = 'Widget with id';
 
alert( str.indexOf('id', 2) ) // 12
Чтобы найти все вхождения подстроки, нужно запустить indexOf в цикле. Каждый раз, получив очередную позицию, начинаем новый поиск со следующей:
 
let str = 'Ослик Иа-Иа посмотрел на виадук';
 
let target = 'Иа'; // цель поиска
 
let pos = 0;
while (true) {
  let foundPos = str.indexOf(target, pos);
  if (foundPos == -1) break;
 
  alert( `Найдено тут: ${foundPos}` );
  pos = foundPos + 1; // продолжаем со следующей позиции
}
Тот же алгоритм можно записать и короче:
 
let str = "Ослик Иа-Иа посмотрел на виадук";
let target = "Иа";
 
let pos = -1;
while ((pos = str.indexOf(target, pos + 1)) != -1) {
  alert( pos );
}
str.lastIndexOf(substr, position)
 

    Также есть похожий метод str.lastIndexOf(substr, position), который ищет с конца строки к её началу.

    Он используется тогда, когда нужно получить самое последнее вхождение: перед концом строки или начинающееся до (включительно) определённой позиции.

При проверке indexOf в условии if есть небольшое неудобство. Такое условие не будет работать:

 
let str = "Widget with id";
 
if (str.indexOf("Widget")) {
    alert("Совпадение есть"); // не работает
}
Мы ищем подстроку "Widget", и она здесь есть, прямо на позиции 0. Но alert не показывается, т. к. str.indexOf("Widget") возвращает 0, и if решает, что тест не пройден.
 
Поэтому надо делать проверку на -1:
 
let str = "Widget with id";
 
if (str.indexOf("Widget") != -1) {
    alert("Совпадение есть"); // теперь работает
}

 

    Более современный метод str.includes(substr, pos) возвращает true, если в строке str есть подстрока substr, либо false, если нет. Это правильный выбор, если нам необходимо проверить, есть ли совпадение, но позиция не нужна:

 
alert( "Widget with id".includes("Widget") ); // true
 
alert( "Hello".includes("Bye") ); // false

    Необязательный второй аргумент str.includes позволяет начать поиск с определённой позиции:

 
alert( "Midget".includes("id") ); // true
alert( "Midget".includes("id", 3) ); // false, поиск начат с позиции 3
Методы str.startsWith и str.endsWith проверяют, соответственно, начинается ли и заканчивается ли строка определённой строкой:
 
alert( "Widget".startsWith("Wid") ); // true, "Wid" — начало "Widget"
alert( "Widget".endsWith("get") ); // true, "get" — окончание "Widget"
 
В JavaScript есть 3 метода для получения подстроки: substring, substr и slice.
 
str.slice(start [, end])
Возвращает часть строки от start до (не включая) end.
 
Например:
 
let str = "stringify";
// 'strin', символы от 0 до 5 (не включая 5)
alert( str.slice(0, 5) );
// 's', от 0 до 1, не включая 1, т. е. только один символ на позиции 0
alert( str.slice(0, 1) );
Если аргумент end отсутствует, slice возвращает символы до конца строки:
 
let str = "stringify";
alert( str.slice(2) ); // ringify, с позиции 2 и до конца
 
Также для start/end можно задавать отрицательные значения. Это означает, что позиция определена как заданное количество символов с конца строки:
 
let str = "stringify";
 
// начинаем с позиции 4 справа, а заканчиваем на позиции 1 справа
alert( str.slice(-4, -1) ); // gif
str.substring(start [, end])
 
Возвращает часть строки между start и end (не включая) end.
 
Это — почти то же, что и slice, но можно задавать start больше end. Если start больше end, то метод substring сработает так, как если бы аргументы были поменяны местами.
 
Например:
 
let str = "stringify";
 
// для substring эти два примера — одинаковы
alert( str.substring(2, 6) ); // "ring"
alert( str.substring(6, 2) ); // "ring"
 
// …но не для slice:
alert( str.slice(2, 6) ); // "ring" (то же самое)
alert( str.slice(6, 2) ); // "" (пустая строка)
 
    Отрицательные значения substring, в отличие от slice, не поддерживает, они интерпретируются как 0.
 
    Метод str.substr(start [, length]) возвращает часть строки от start длины length.
 
В противоположность предыдущим методам, этот позволяет указать длину вместо конечной позиции:
 
let str = "stringify";
// ring, получаем 4 символа, начиная с позиции 2
alert( str.substr(2, 4) );
 

    Значение первого аргумента может быть отрицательным, тогда позиция определяется с конца:

 
let str = "stringify";
// gi, получаем 2 символа, начиная с позиции 4 с конца строки
alert( str.substr(-4, 2) );
 

    Этот метод находится в Annex B спецификации языка. Это означает, что его должны поддерживать только браузерные движки JavaScript, и использовать его не рекомендуется. Но на практике он поддерживается везде.

    Алгоритм сравнения строк сложнее, чем может показаться, так как разные языки используют разные алфавиты. Поэтому браузеру нужно знать, какой язык использовать для сравнения. К счастью, все современные браузеры (для IE10− нужна дополнительная библиотека Intl.JS) поддерживают стандарт ECMA 402, обеспечивающий правильное сравнение строк на разных языках с учётом их правил.

    Для этого есть соответствующий метод.

    Вызов str.localeCompare(str2) возвращает число, которое показывает, какая строка больше в соответствии с правилами языка:

  • Отрицательное число, если str меньше str2.
  • Положительное число, если str больше str2.
  • 0, если строки равны.

Например:

 
alert( 'Österreich'.localeCompare('Zealand') ); // -1

    У этого метода есть два дополнительных аргумента, которые указаны в документации. Первый позволяет указать язык (по умолчанию берётся из окружения) — от него зависит порядок букв. Второй — определить дополнительные правила, такие как чувствительность к регистру, а также следует ли учитывать различия между "a" и "á".