Найти в Дзене
Я, Golang-инженер

#51. Проверка сложности пароля пакетом regexp

Оглавление

Это статья об основах программирования на Go. На канале я рассказываю об опыте перехода в IT с нуля, структурирую информацию и делюсь мнением.

Хой, джедаи и амазонки!

Когда мы регистрируемся на сайтах, нам предлагают ввести пароль и сообщают о надёжности вводимого пароля. В публикации поделюсь идеей реализации подобного простенького алгоритма с применением стандартного пакета Go regexp.

1. Задача

Сама задача взята с курса stepik "Программирование на Golang", подраздел "Строки".

Условия:

Ваша задача сделать проверку подходит ли пароль вводимый пользователем под заданные требования. Длина пароля должна быть не менее 5 символов, он должен содержать только арабские цифры и/или буквы латинского алфавита.
На вход подаётся строка-пароль.
Если пароль соответствует требованиям - вывести "Ok", иначе вывести "Wrong password"

Мой вариант решения выглядит так:

Решение задачи
Решение задачи

Разберём что здесь что. Привычных вещей вроде операторов if или считывания с консоли - касаться не будем. Поделюсь только тем, что считаю интересным.

2. Функция len

Начнём с простого. Функция len в строке кода 12 вернёт нам длину строки (password) в байтах. Т.е. не количество символов (рун), а именно количество байт, которые хранят в себе содержимое строки password. Напоминаю, под собой строка содержит срез байтов. Подробнее об этом можно почитать здесь <<<

Если кратко, то символ - это какой либо символ, в т.ч. смайлик или иероглиф из таблицы символов Unicode, или, например, символ точки или буква N из таблицы символов ASCII - которая также входит в состав таблицы Unicode.
Руна - это целое число, которое соответствует коду символа в таблице Unicode. Руна может занимать от 1 до 4 байт.
Соответственно, пароль состоящий из пяти символов Unicode может требовать для хранения от 5 до 20 байт.

Конкретно для заданных условий, функции len будет достаточно, т.к. мы проверяем по сути наличие в получаемом на входе пароле наличие только символов, входящих в ASCII-таблицу. А они в свою очередь требуют 1 байт для хранения каждый. Поэтому мы просто проверяем длину пароля более 5 байт.

Другое дело, если были бы условия, что в пароле могут быть символы из кириллицы. А то и прочие символы таблицы Unicode. В этом случае проверка на количество символов была бы сложнее. Как это сделать?

2.1. Вариант первый

Вместо len(password) нужно было бы записать:

utf8.RuneCountInString(password)

А в импортируемые пакеты к fmt добавить:

"unicode/utf8"

Функция utf8.RuneCountInString() является частью пакета utf8 в стандартной библиотеке Go и используется для определения количества рун в заданной строке. Эта функция обрабатывает строку, учитывая кодировку Unicode, чтобы правильно подсчитывать количество рун.

2.2. Вариант второй

Вместо len(password) нужно записать:

len([]rune(str))

И всё - никаких внешних пакетов импортировать не нужно.

Преобразование строки в руны может быть выполнено с использованием функции []rune().

Если мы захотим вывести на печать строку, например "АБВ", преобразованную в руну, то получим [1040 1041 1042].

Собственно, этот вариант лаконичнее подгружения пакета unicode/utf8, поэтому предпочтительнее.

3. Пакет regexp

Пакет regexp (сокращение от англ. Regular Expression - регулярное выражение) - это пакет в стандартной библиотеке Go, который предоставляет функционал для работы с регулярными выражениями.

Рассмотрим строку 9 кода на иллюстрации (см. раздел 1):

re := regexp.MustCompile("^[0-9A-Za-z]+$")

Здесь применяется пакет regexp. Официальная документация на пакет здесь <<<

О regexp
О regexp

3.1. О пакете

Регулярное выражение - это строка (набор символов), которая содержит шаблон для поиска или замены текста. Оно используется для поиска подстрок в тексте, проверки соответствия формату данных и других задач.

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

Пакет regexp в Go позволит создавать, компилировать и использовать регулярные выражения для обработки текста.

Некоторые основные функции и методы пакета regexp включают:

  1. Функция regexp.Compile() или метод Compile() - для компиляции регулярного выражения и умеет возвращать ошибку.
  2. Методы MatchString(), Match(), FindString(), FindStringIndex() и другие - для проверки соответствия регулярному выражению и поиска соответствующих совпадений в строке.
  3. Методы FindAllString(), FindAllStringSubmatch(), FindAll() и другие - для поиска всех совпадений регулярного выражения в строке.
  4. Функция regexp.MustCompile() - для компиляции регулярного выражения без необходимости явной обработки ошибок. Отличается от regexp.Compile() тем, что при ошибке компиляции регулярного выражения вызывает панику.
  5. Методы ReplaceAllString(), ReplaceAllStringFunc(), ReplaceAll() и другие - для замены совпадений регулярного выражения в строке на другие значения.
  6. Методы Split(), SplitN() и другие - для разделения строки на подстроки на основе регулярного выражения.

Функцию regexp.MustCompile() удобно использовать, когда мы уверены в правильности написания регулярного выражения, и не хотим обрабатывать ошибки компиляции. Однако, если мы не уверены в правильности написания регулярного выражения, лучше использовать regexp.Compile() и обработать возможную ошибку.

3.2. Порядок работы с пакетом

1. В моём примере кода я использовал пакет regexp для создания регулярного выражения с помощью regexp.MustCompile(). Напомню эту строчку:

re := regexp.MustCompile("^[0-9A-Za-z]+$")

Данное регулярное выражение будет соответствовать строкам, которые состоят только из арабских цифр и букв латинского алфавита верхнего и нижнего регистров.

Символ "^" обозначает начало строки, символ "$" - конец строки, а "+" - одно или более вхождений предшествующего символа или группы символов.

2. Функция re.MatchString(password) возвращает true, если в исходной строке (password) присутствуют символы, скомпилированные в строке re. Иначе - false.

Что здесь можно добавить. Когда используют regexp, то обычно используют один паттерн (шаблон - если хотите - здесь он [0-9A-Za-z]) много раз - а это почти всегда так, то полезно его скомпилировать и потом пользоваться им. В моём коде так и сделано.

Дело в том, что компиляция шаблона занимает довольно много время. Можете задать вопрос - а как плохо? Вот так делать не нужно:

Фрагмент кода
Фрагмент кода

Здесь компиляция регулярного выражения происходит постоянно при подаче на проверку нового пароля. Это не есть хорошо.

Циклы по 100 раз я добавил для эксперимента - посмотреть что и как.

В среднем, в "плохом" коде длительность компиляции регулярное выражение (строка 63 кода) составляет 487.302µs (микросекунд) за сто циклов. А проверка в строках кода 47-53 - 513.95µs.

В первом же примере эти значения в диапазоне 100-300 микросекунд на сто циклов. Но вот какая штука - если скомпилировать строку, то при проверке, скажем, сотни или ста тысяч паролей - не придётся компилировать регулярное выражение снова и снова, если использовать код из первого примера.

4. Выводы

Познакомились с ещё одним пакетом в Go - regexp (легко запомнить - regular expression). Пополняем нашу инженерную насмотренность :)

Предлагаю самостоятельно улучшить предложенный алгоритм:

  1. Обеспечить, чтобы программа выдавала "Ок" - в случае, если есть комбинация и цифр и букв (сейчас можно ввести только цифры, и будет "Ок").
  2. Сделать проверку на пароль "Простой", "Средний" и "Сложный". Например, по количеству символов: 5 - простой, 6-10 - средний, более 11 - сложный.
  3. Добавить функционал ввода символов из Unicode (в плане подсчёта количества символов и наличия символа не только из ASCII).
Alex Mihai C 3alexander https://unsplash.com/photos/RMMWgYdtaEc
Alex Mihai C 3alexander https://unsplash.com/photos/RMMWgYdtaEc

Бро, ты уже здесь? 👉 Подпишись на канал для новичков «Войти в IT» в Telegram, будем изучать IT вместе 👨‍💻👩‍💻👨‍💻