[перевод] 10 самых задаваемых вопросов о строках в java

Создание строк

По сути, в JavaScript есть две категории строк: строковые примитивы и объекты String.

Примитивы

Строковые примитивы создаются следующими способами:

Почти во всех случаях вы должны использовать один из этих методов для создания новой строки.

При определении строкового литерала можно использовать одинарные кавычки (‘ ‘) или двойные кавычки (» «).

Объекты

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

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

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

Метод сравнения String compareTo()

Метод сравнения compareTo() применяется, если надо определить лексикографический порядок строк. Он выполняет сравнение значения char, действуя аналогично equals(). Когда 2 строки совпадают, compareTo() вернёт значение «ноль» (результат = 0). Сравнивая 2 строки, он вернёт положительное целое число (результат > 0), если 1-й объект String следует за 2-й строкой. Соответственно, метод вернёт отрицательный результат (результат < 0), когда 1-й объект String будет предшествовать 2-й строке:

result1 == result2  возвращается ;
    result1 > result2   возвращается положительное значение;
    result1 < result2    возвращается отрицательное значение.

Приведём пример:

class TestClass{
  public static void main (String[] args)   {
    String str1 = "Java";
    String str2 = "Java";
    String str3 = "ASP";
    int val = ;
    val = str1.compareTo(str2);
    System.out.println(val);
    val = str1.compareTo(str3);
    System.out.println(val);
    val = str3.compareTo(str1);
    System.out.println(val);
  }
}

Итог:

9
-9

На этом всё, очень надеемся, что этот материал будет вам полезен при сравнении строк в «Джава».

При подготовке статьи использовалась публикация «String Comparison in Java».

Хотите знать больше? Приходите на курс!

.

Полные и сокращённые версии AND и OR

&& и || называются сокращёнными логическими операторами AND и OR соответственно, или операторами короткой схемы вычислений. В спецификации Java их ещё зовут условными. Значения их операндов могут быть только булева типа.

В отличие от двойных, одиночные & и | называются операторами полной схемы вычислений. Значения их операндов могут быть как только булевыми, так и только целочисленными (вместе с оператором ^ они используются в побитовых операциях).

В чём разница

В том, что для операторов & и | всегда вычисляются значения обоих операндов, а при работе операторов && и || второй операнд вычисляется только по необходимости.

То есть иногда результат выражения однозначно определён уже по первому операнду:

  1. Если первый операнд && равен false, то второй не вычисляется, так как уже понятно, что результат всего выражения будет false.
  2. Если первый операнд || равен true, то второй не вычисляется, так как уже понятно, что || вернёт true.

&& и || используют как операторы булевой логики. Они оперируют значениями только булева типа и применяются только в логических выражениях.

Как использовать

&& и || позволяют экономить вычисления (применять короткую схему) и помогают избегать ошибок. Как это делается?

Начнём с оператора &&. Приведём фрагмент из таблицы выше:

Логический оператор Обозначение в Java Выражение Результат
«И» (AND): конъюнкция, логическое умножение && true && truefalse && falsetrue && falsefalse && true truefalsefalsefalse

Рассмотрим выражение: (3 > 4) AND (5 > 4)

Мы видим, что операнд слева от оператора AND равен false. Смотрим на таблицу выше — и понимаем, что вычислять второй операнд бессмысленно, так как оператор AND уже точно вернёт false.

Именно по такой логике и работает оператор короткой схемы вычислений &&. Если выражение слева от него равно false, то выражение справа вычисляться не будет.

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

В большинстве случае применяют именно && и ||. При верном использовании они избавляют Java от ненужных вычислений и страхуют от некоторых ошибок.

Первый пример

Если вместо оператора && мы используем &, то получим ошибку (исключение) java.lang.ArithmeticException: / by zero:

Ошибка возникнет тогда, когда Java попытается вычислить второй аргумент логического выражения, если первый равнялся false.

Иными словами, мы узнали, что b равно 0 (выражение b != 0 вернуло false) — и идём делить на b (делить на ноль), вычисляя значение второго операнда (a/b > 0).

Второй пример

Код выше выводит в консоль длину строки str, в которой есть хотя бы один символ. А если строка пуста или её значение равно null (то есть строковая переменная ни на что не указывает), в консоль выводится сообщение: «Тут нечего считать!»

Мы выбрали оператор короткой схемы вычислений && — и это правильно!

А вот если бы вместо этого использовали оператор полной схемы &, то наш код работал бы не так, как надо.

Мы получали бы ошибку NullPointerException каждый раз, когда вызываем метод для строковой переменной со значением null.

Посмотрим, что происходило бы при вычислении условия блока if:

  1. str != null & str.length() > 0
  2. null != null & str.length() > 0
  3. false & str.length() > 0 // тут возникает ошибка

Сперва вычисляется первый аргумент логического выражения, а именно str != null (иными словами, получаем ответ на вопрос «Строковая переменная не равна null?»). Получили false, значит всё же равна.

Дальше Java должна вычислить второй аргумент логического выражения, а именно str.length() > 0 (иными словами — проверяется «Число символов строки > 0?»).

Создание подстрок

Кроме сравнения строк и поиска подстрок, есть еще одно очень популярное действие — получение подстроки из строки. В предыдущем примере вы как раз видели вызов метода , который возвращал часть строки.

Вот список из 8 методов получения подстрок из текущей строки:

Методы Описание
Возвращает подстроку, заданную интервалом символов .
Повторяет текущую строку n раз
Возвращает новую строку: заменяет символ на символ
Заменяет в текущей строке подстроку, заданную регулярным выражением.
Заменяет в текущей строке все подстроки, совпадающие с регулярным выражением.
Преобразует строку к нижнему регистру
Преобразует строку к верхнему регистру
Удаляет все пробелы в начале и конце строки

Вот краткое описание существующих методов:

Метод

Метод возвращает новую строку, которая состоит из символов текущей строки, начиная с символа под номером и заканчивая . Как и во всех интервалах в Java, символ с номером в интервал не входит. Примеры:

Код Результат

Если параметр не указывается (а так можно), подстрока берется от символа beginIndex и до конца строки.

Метод

Метод repeat просто повторяет текущую строку раз. Пример:

Код Результат

Метод

Метод возвращает новую строку, в которой все символы заменены на символ . Длина строки при этом не меняется. Пример:

Код Результат

Методы и

Метод заменяет все вхождения одной подстроки на другую. Метод заменяет первое вхождение переданной подстроки на заданную подстроку. Строка, которую заменяют, задается регулярным выражением. Разбирать регулярные выражения мы будем в квесте Java Multithreading.

Примеры:

Код Результат

Методы

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

Метод

Метод удаляет у строки пробелы с начала и с конца строки. Пробелы внутри строки никто не трогает. Примеры:

Код Результат

Сравнение строк: equals() или ==?

Хотя в двух переменных содержится одно и то же слово, мы имеем дело с двумя разными объектами и оператор == вернёт false.

Однажды, когда деревья были большими, мне понадобилось сравнить две строки из разных источников. Хотя строки выглядели совершенно одинаково, сравнение при помощи оператора == возвращало false и путало мне все карты. И только потом я узнал, что нужно использовать метод equals(). Строка в Java — это отдельный объект, который может не совпадать с другим объектом, хотя на экране результат выводимой строки может выглядеть одинаково. Просто Java в случае с логическим оператором == (а также !=) сравнивает ссылки на объекты (при работе с примитивами такой проблемы нет):

Поиск в строке

Метод indexOf() находит
индекс первого вхождения подстроки в строку, а метод lastIndexOf() — индекс
последнего вхождения. Если подстрока не будет найдена, то оба
метода возвращают -1:

String str = "Hello world";
int index1 = str.indexOf('l'); // 2
int index2 = str.indexOf("wo"); //6
int index3 = str.lastIndexOf('l'); //9
 
System.out.println(index1+" "+index2+" "+index3);

Метод startsWith() позволяют
определить начинается ли строка с определенной подстроки, а метод endsWith() позволяет
определить заканчивается строка на определенную подстроку:

String str = "myfile.exe";
boolean start = str.startsWith("my"); //true
boolean end = str.endsWith("exe"); //true
 
System.out.println(start+" "+end);

Оператор для сравнения строк «==»

В первую очередь, надо сказать, что этот оператор проверяет и сравнивает не значения, а ссылки. С его помощью вы сможете проверить, являются ли сравниваемые вами элементы одним и тем же объектом. Когда 2 переменные String указывают на тот же самый объект в памяти, сравнение вернёт true, в обратном случае — false.

"Java" == "Java" //true

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

new String("Java") == "Java" // false

Вышеприведённые переменные String указывают уже на различные объекты.

new String("Java") == new String("Java") // false

Здесь тоже вышеприведенные переменные String указывают на различные объекты.

Итак, мы видим, что оператор == по сути, сравнивает не две строки, а лишь ссылки, на которые указывают строки.

class TestClass{
  public static void main (String[] args){
    // ссылается на тот же объект, возвращая true
    if(  "Java" == "Java" ){
      System.out.println("Statement  is true");
    }else{
      System.out.println("Statement is false");
    }
    // указывает уже на другой объект, возвращая false
    if(new String("Java") == "Java"){
      System.out.println("Statement  is true");
    }else{
      System.out.println("Statement is false");
    }
    // указывает тоже на другой объект, возвращая false
    if(new String("Java") == new String("Java") ){
      System.out.println("Statement  is true");
    }else{
      System.out.println("Statement is false");
    }
  }
}

Итог:

Statement  is true
Statement is false
Statement is false

Метод сравнения String equals()

Сравнение строк с помощью equals позволяет проверять исходное содержимое строки. Метод возвращает true, когда параметр — объект String, представляющий собой ту же строку символов, что и объект:

Objects.equals("Java", new String("Java")) //true

Когда надо выполнить проверку, имеют ли 2 строки одинаковое значение, мы можем задействовать Objects.equals().

class TestClass{
  public static void main (String[] args)   {
    String str1 = "Java";
    String str2 = "Java";
    String str3 = "ASP";
    String str4 = "JAVA";
    String str5 = new String("Java");
    // оба равны и возвращают true
    if(str1.equals(str2)){
      System.out.println("Statement  is true");
    }else{
      System.out.println("Statement is false");
    }
    // оба не равны и возвращают false
    if(str1.equals(str3)){
      System.out.println("Statement  is true");
    }else{
      System.out.println("Statement is false");
    }
    // оба не равны и возвращают false
    if(str1.equals(str4)){
      System.out.println("Statement  is true");
    }else{
      System.out.println("Statement is false");
    }
    // оба равны и возвращают true
    if(str1.equals(str5)){
      System.out.println("Statement  is true");
    }else{
      System.out.println("Statement is false");
    }
  }
}

Итог:

Statement  is true
Statement is false
Statement is false
Statement  is true

Как отсортировать список строк по их длине в Java 7 и 8

Ниже приведен пример использования этого компаратора длины строк для сортировки списка строк по их длине. В этом примере мы продемонстрировали программы на JDK 6 и 7 с использованием анонимного класса, и новый способ с использованием лямбда-выражений, который доступен в Java 8. Он занимает всего одну строку и его намного проще понять, если вы знакомы с синтаксисом лямбда-выражений.

Как отсортировать список строк по их длине в Java 7 и 8

Java 6, 7

Import java.util.ArrayList;
Import java.util.Arrays;
Import java.util.Collections;
Import java.util.Comparator;
Import java.util.List;

/ *
 * Программа Java для сортировки списка строк по их длине
 * /

public class StringComparisonByLength{

  Public static void main (String [] args) {

List<String> books = new ArrayList<>(Arrays.asList("Effective Java", "Algorithms", "Refactoring" ));
System.out.println("Sorting List of String by length in JDK 7 ======");
System.out.println("The original list without sorting");
System.out.println(books);

    Comparator<String> byLength = new Comparator<String>(){
      @Override
      Public int compare (String s1, String s2) {
        Return s1.length () - s2.length ();
        }
    };

    Collections.sort(books, byLength);
    System.out.println("The same list after sorting string by length");

    System.out.println(books);
  }
}

Результат

Sorting List of String by length in JDK 7 ======
The original list without sorting 
The same list after sorting string by length

Java 8

Import java.util.ArrayList;
Import java.util.Arrays;
Import java.util.Comparator;
Import java.util.List;
/ *
 * Программа Java для сортировки списка строк по их длине в JDK 8
 * /

public class SortingListOfStringByLength{

  public static void main(String[] args) {

    // В Java 8
    System.out.println("Sorting List of String by length in Java 8 ======");

   List<String> cities = new ArrayList<>(Arrays.asList("London", "Tokyo", "NewYork"));
    System.out.println("The original list without sorting");
    System.out.println(cities);

    cities.sort((first, second) -> Integer.compare(first.length(), second.length()));

    System.out.println("The same list after sorting string by length");
    System.out.println(cities);
  }
}

Результат

Sorting List of String by length in Java 8 ====== 
The original list without sorting 
 
The same list after sorting string by length 

Это все, что касается сравнения длины строк Java и сортировки списка String по длине в Java 7 и Java 8. В последней версии языка делать это намного удобнее, поскольку можно создать пользовательский компаратор только одной строкой, используя лямбда-выражение. API JDK 8 содержит множество подобных методов, которые облегчают еще более сложные сравнения. Например, используя метод thenComparing(), можно связать несколько компараторов.

Замена символов replace()

Класс Java String содержит метод replace(), который может заменять символы в строке. Он фактически не заменяет символы в существующей строке. Скорее, возвращает новый экземпляр String. Он равен экземпляру String, из которого он был создан, но с заменой указанных символов. Пример:

String source   = "123abc";
String replaced = source.replace('a', '@');

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

123@bc

Метод replace() заменит все символы, соответствующие символу, переданному методу в качестве первого параметра, вторым символом, переданным в качестве параметра.

Java string методы — использование оператора ==

Оператор == проверяет ссылки, а не значения. Это означает, что он проверяет, являются ли сравниваемые элементы одним и тем же объектом. Если две переменные String указывают на один и тот же объект в памяти, сравнение возвращает true. В противном случае — false:

"Java" == "Java" //true

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

new String("Java") == "Java" // false

Приведенные выше переменные String указывают на разные объекты:

new String("Java") == new String("Java") // false

Приведенные выше переменные String также указывают на разные объекты.

Оператор ‘==’ не сравнивает строки в java, а только ссылки, на которые они строки.

Пример

class TestClass{
  public static void main (String[] args){
    // ссылается на один и тот же объект, возвращает true
    if(  "Java" == "Java" ){
      System.out.println("Statement  is true");
    }else{
      System.out.println("Statement is false");
    }
    // указывает на другой объект, возвращает false
    if(new String("Java") == "Java"){
      System.out.println("Statement  is true");
    }else{
      System.out.println("Statement is false");
    }
    // указывает на другой объект, возвращает false
    if(new String("Java") == new String("Java") ){
      System.out.println("Statement  is true");
    }else{
      System.out.println("Statement is false");
    }
  }
}

Результат

Statement  is true
Statement is false
Statement is false

Устройство класса String

Сегодня мы поговорим о классе . Класс String — самый популярный класс в Java после типа int. Он используется абсолютно везде. У него есть куча полезных методов, которые лучше знать, чем не знать.

Класс — единственный класс, кроме примитивных типов, литералы которого можно использовать в ; компилятор по-особому обрабатывает сложение строк и объектов; объекты по-особому хранятся в памяти. В общем, класс — это очень специфический класс.

Также у класса есть куча классов-сателлитов, цель которых — еще больше упростить работу со строками в Java. Когда вы изучите все это, вам действительно станет значительно проще делать многие вещи. Ну а начнем мы с самого сердца этой экосистемы — с устройства класса .

Массив символов

А устроен класс на самом деле очень просто: внутри него находится массив символов (char), который хранит все символы строки. Вот так, например, хранится слово «Привет»:

Важно!

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

Преобразование к строковому типу

Как уже говорилось выше, разработчики Java сделали так, что абсолютно все переменные, объекты, выражения в Java можно преобразовать к типу .

Более того, это автоматически происходит, когда мы складываем тип с каким-нибудь другим типом. Примеры:

Команда Примечание
содержит строку
содержит строку
содержит строку

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

Арифметические операции с типом проводить нельзя. Даже если строка целиком состоит из цифр.

Примеры:

Команда Примечание
содержит строку
содержит строку
содержит строку

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

Команда Примечание
содержит строку

Порядок выполнения:

Метод String.format() и класс StringFormatter

И еще один интересный метод класса String — .

Допустим, у вас есть различные переменные с данными. Как вывести их на экран одной строкой? Например, у нас есть данные (левая колонка) и желаемый вывод (правая колонка):

Код Вывод на экран

Скорее всего, ваш код будет выглядеть примерно так:

Код программы

Такой код не слишком читабельный. Более того, если бы имена переменных были длиннее, код стал бы еще сложнее:

Код программы

Не очень читаемо, не так ли?

Однако в реальных программах такая ситуация встречается часто, поэтому я хочу рассказать о способе, как проще и короче записать этот код.

У класса String есть статический метод : он позволяет задать шаблон объединения строки с данными. Общий вид этой команды такой:

Пример:

Код Результат

В метод первым параметром передают строку-шаблон, которая содержит весь нужный текст, а в местах, где нужно вставлять данные, написаны специальные символы типа , и т.п.

Вот эти и метод и заменяют на параметры, которые идут следом за строкой-шаблоном. Если нужно подставить строку, мы пишем , если число — . Пример:

Код Результат
будет равна

Вот краткий список параметров, которые можно использовать внутри шаблона:

Символ Обозначение
целое число: , , ,
вещественное число: ,
Символ

Эти параметры указывают на тип данных, но есть еще параметры, которые указывают на порядок данных. Чтобы взять параметр по его номеру (нумерация начинается с единицы), нужно записать вместо . Пример:

Код Результат
будет равна

возьмет 3-й параметр-переменную, возьмет второй параметр. возьмет самый первый параметр-переменную. Параметры шаблона , обращаются к переменным-параметрам независимо от параметров шаблона типа или

Как изменить порядок вычисления

Порядок вычисления логических операторов меняют круглые скобки — так же, как в арифметике:

Добавив круглые скобки, мы поменяли приоритеты для вычисления. Теперь сперва будет определено выражение (true ^ true), которое вернёт false. А после — вычислится выражение false & false, которое тоже вернёт false.

То есть скобки повышают приоритет стоящего внутри выражения, а внутри самих скобок действуют прежние приоритеты.

Пример посложнее — выражение !(true && (false || true)) ^ !false.

Порядок вычисления:

  1. !(true && (false || true)) ^ !false
  2. !(true && true) ^ !false
  3. !true ^ !false
  4. false ^ !false
  5. false ^ true
  6. true

Шаблонные строки

Базовые шаблонные строки

Шаблонные строки позволяют объединять переменные и текст в новую строку с использованием более удобочитаемого синтаксиса.

Fullstack JavaScript Developer

Health Samurai, Санкт-Петербург, можно удалённо, От 150 000 до 200 000 ₽

tproger.ru

Вакансии на tproger.ru

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

До

После

Вы также можете включать выражения в шаблонные строки:

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

Chrome: 41+

Edge: 13+

Firefox: 34+

Safari: 9.1+

Opera: 29+

Разбиение текста

  • Метод разбивает в соответствии с найденными соответствиями шаблону объекта и возвращает результаты в массиве. Каждый элемент массива задает текстовую последовательность, отделенную от следующей последовательности соответствующим шаблону фрагментом текста (или концом текста). Элементы массива находятся в том же порядке, в котором они встречаются в .

    В этом методе, количество элементов массива зависит от параметра , контролирующего также и число искомых соответствий.

    • При положительном значении выполняется поиск не более чем соответствий, а длина массива не превышает элементов.
    • При отрицательном значении выполняется поиск всех возможных соответствий, и длина массива может быть произвольной.
    • При равном нулю значении выполняется поиск всех возможных соответствий, длина массива может быть произвольной, а пустые строки в конце отбрасываются.
  • Метод вызывает предыдущий метод с 0 в качестве аргумента limit и возвращает результат его вызова.

Преобразование с использованием Integer.toString(int)

Класс Integer имеет статический метод, который возвращает объект String, представляющий параметр int, указанный в функции Integer.toString(int). Этот подход, в отличие от других, может возвращать исключение NullPointerException.

Синтаксис

Есть два разных выражения для метода Integer.toString():

public static String toString(int i)

public static String toString(int i, int radix)

Параметры

Параметры этого метода:

  • i: целое число, которое будет преобразовано.
  • radix: используемая система счисления базы для представления строки.

Возвращаемое значение

Возвращаемое значение для обоих выражений – строка Java, представляющая целочисленный аргумент «i». Если используется параметр radix, возвращаемая строка определяется соответствующим основанием.

Пример

package MyPackage;
public class Method1 
{
	public static void main(String args[]) 
	{
		  int n = Integer.MAX_VALUE;
		  String str1 = Integer.toString(n);
		  System.out.println("The output string is: " + str1);
		  int m = Integer.MIN_VALUE;
		  String str2 = Integer.toString(m);
		  System.out.println("The output string is: " + str2);
	}
	 
}

Через DecimalFormat

DecimalFormat – это конкретный подкласс класса NumberFormat, который форматирует десятичные числа. Он имеет множество функций, предназначенных для анализа и форматирования чисел. Вы можете использовать его для форматирования числа в строковое представление по определенному шаблону.

Пример

import java.text.DecimalFormat;
public class Method4
{
	 public static void main(String[] args) 
	 {
	      int number = 12345;
	      DecimalFormat numberFormat = new DecimalFormat("##,###");
	      String str = numberFormat.format(12345);	      
          System.out.println("The number to be converted is: " + number);
	      System.out.println("The string version of 12345 is: " + str);
	 }
	 
}

Вывод

The number to be converted is: 12345
The string version of 12345 is: 12,345

Если вы знаете, как использовать метод DecimalFormat, это лучший вариант для преобразования Integer в String из-за уровня контроля, который можете иметь при форматировании. Можете указать количество знаков после запятой и разделитель запятых для лучшей читаемости, как показано в примере выше.

Методы класса String

У класса очень много методов: одних только конструкторов у него 18 штук! Поэтому ниже мы приведем только самые основные из них:

Методы Описание
Возвращает количество символов в строке
Проверяет, что строка == пустая строка
Проверяет, что в строке — только whitespace-символы: пробел, tab, enter и т.п.
Возвращает символ, который стоит на index-позиции в строке.
Возвращает массив символов (копию), из которых состоит строка
Преобразует строку в набор байт и возвращает массив байт.
Разделяет строку на несколько подстрок.
Склеивает вместе несколько подстрок
Помещает строку в пул .

Больше о конструкторах вы можете узнать из статьи Зачем нужен конструктор?

Давайте напишем программу, которая преобразовывает путь к файлу из Unix Style в Windows Style. Unix в качестве разделителя директорий использует символ , Windows — символ .

Решение 1 — использование массива char’ов

Код Примечания
Создание объекта Scanner
Чтение строки с консоли
Преобразование строки в массив символов
Цикл по символам
Если символ равен ,
заменить его на . Не забываем про экранирование.
Создаем новую строку на основе массива символов.
Выводим строку на экран.

Решение 2 — использование методов и :

Код Примечания
Создание объекта Scanner
Чтение строки с консоли
Преобразование строки в массив строк. В качестве разделителя используется символ (дополнительные два слеша – это следствие двойного экранирования).
Объединяем все строки из массива строк, в качестве разделителя используется символ (мы видим его экранированным).
Выводим строку на экран.

Решение 3 — использование метода :

Код Примечания
Создание объекта Scanner
Чтение строки с консоли
Просто заменяем один символ на второй
(второй — экранирован)
Выводим строку на экран.
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector