Roman Yatsenko
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: Тема 9. Регулярные выражения tags: Python Advanced slideOptions: allottedMinutes: 80 transition: slide theme: beige slideNumber: c spotlight: enabled: true --- ## ++Тема 9++<br>Регулярные выражения (c) Яценко Р.Н., 2019-2021 [Учебный центр компьютерных технологий "Кит"](http://kit.kh.ua/) <img src="https://i.imgur.com/Kh901c1.png" style="width: 150px; position: fixed; top: 10px; right: 10px; border: 0; box-shadow: none;"> --- ## 1. Введение в регулярные выражения (re) ---- ### Назначение - Проще говоря, регулярное выражение используется для поиска паттернов в указанной строке. Паттерном может быть все что угодно - Можно создавать паттерны соответствия электронной почте или мобильному номеру. Можно создать паттерны, которые ищут слова в строке, начинающиеся на “a” и заканчивающиеся на “z” ---- ### Где используются 1. Проверка соответствия фрагментов текста некоторым критериям. Например, наличие символа обозначения валюты и последующих за ним цифр, проверка адреса электронной почты и т.д. 2. Поиск подстрок, которые в том числе могут иметь несколько форм. Например, поиск `'pet.png'`, `'pet.jpg'`, `'pet.jpeg'` или `'pet.svg'`, но не обнаруживались подстроки `'carpet.png'` и подобные ей ---- 3. Замена всего, что совпадает с шаблоном, на указанную строку. Например, поиск подстроки `'устройство передвижения, движимое мускульной силой'` и замена подстрокой `'велосипед'` 4. Разбиение строки по точкам совпадения с шаблоном. Например, разбиение строки по `':'` или `'='` ---- ### Пример кода ```python= import re pattern = r"times" string = "It was the best of times, it was the worst of times." print(len(re.findall(pattern,string))) # 2 ``` ---- ### Примеры выражений Для написания и отладки регулярных выражений удобно использовать онлайн-сервисы, например https://regex101.com, которые в том числе содержат библиотеку готовых выражений и могут генерировать код для различных языков программирования https://regex101.com/r/HDScOu/1 --- ## 2. Язык регулярных выражений ---- ### Определение [Регулярные выражения](https://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D0%B5_%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F) (_англ._ Regular Expressions) : это компактная форма записи (шаблон, маска или паттерн) представления о коллекции строк. Гибкость и мощь регулярных выражений обусловлена тем, что единственное регулярное выражение может представлять неограниченное число строк, подходящий под заданный шаблон ---- ### Строка-шаблон 1. Флаги 2. Символы и классы символов 3. Квантификаторы 4. Группировка и выбор 5. Проверка границ (привязки) ---- ### Флаги Флаги не входят непосредственно в регулярное выражение, однако расширяют его функции: * `'g'` -- глобальный поиск (обрабатываются все совпадения с шаблоном поиска) * `'i'` -- регистр букв не имеет значения (по умолчанию любой поиск регистрозависим) * `'m'` -- многострочный поиск * и др. Флаг указывается после паттерна, например: `'/[0-9]/m'` ---- ### Поиск символов и строк Одним из наиболее простых случаев является поиск отдельных символов и строк ![](https://i.imgur.com/vJCGdXM.png) https://regex101.com/r/Y67E75/1 ---- ### Экранирование специальных символов Специальные символы ([символы-джокеры](https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB%D1%8B-%D0%B4%D0%B6%D0%BE%D0%BA%D0%B5%D1%80%D1%8B) должны экранироваться символом обратного слеша `'\'`. К ним относятся: ``` \ . ~ $ ? + * { } [ ] ( ) | ``` В пределах регулярных выражений можно также использовать большинство стандартных экранированных последовательностей языка Python, например, `'\n'`, `'\t'` и др. ---- ### Поиск множества символов Для поиска не конкретной последовательности символов, а некоторого их множества предназначены классы символов [группа_символов] : Соответствует любому одиночному символу, входящему в группа_символов. По умолчанию при сопоставлении учитывается регистр ---- *Пример:* - выражение `'[аеёиоуэюя]'` позволяет найти все гласные буквы в строке - выражение `'п[ое]л'` даст совпадение для слов `'пел'` и `'пол'`, но не найдет слова `'поел'` или `'пил'` ---- [^группа_символов] : Соответствует любому одиночному символу, НЕ входящему в `группа_символов`. По умолчанию при сопоставлении учитывается регистр. *Пример:* * выражение `'[^аеёиоуэюя]'` обнаружит все символы в тексте, кроме гласных букв ---- [первый-последний] : Диапазон символов: соответствует одному символу в диапазоне от `первый` до `последний` *Пример:* * выражение `'[0-9]'` найдет все цифры в тексте ---- `.` (точка) : Специальный знак `'.'` соответствует какому-либо одному знаку, кроме `'\n'`. Для поиска точки необходимо использовать экранирование: `'\.'` *Пример:* * выражение `'м.л'` найдет в тексте `'мел'`, `'мул'` и т.д. ---- `\w` : Соответствует любому алфавитно-цифровому знаку *Пример:* * выражение `'\w1'` найдет в тексте `'11'` или `'я1'` `\W` : Соответствует любому символу, НЕ являющимся алфавитно-цифровым знаком ---- `\s` : Соответствует любому пробельному символу (пробел, табуляция и др.) `\S` : Соответствует любому знаку, НЕ являющемуся пробельным ---- `\d` : Соответствует любой десятичной цифре *Пример:* * выражение `'\d-й'` найдет в тексте `'1-й'` или `'2-й'` `\D` : Соответствует любому символу, НЕ являющемуся десятичной цифрой ---- ### Квантификаторы Зачастую требуется не только найти слово по символам или группе символов, но и указать количество возможных повторений, для чего в регулярных выражениях используются квантификаторы Квантификаторы записываются после символа/строки/множества ---- ### Виды квантификаторов Квантификатор|Описание -|- `'*'`|Ноль или более совпадений `'+'`|Одно или больше совпадений `'?'`|Ноль или одно совпадение `'{m}'`|Совпадение ровно `m` раз `'{m,}'`|Совпадение `m` и более раз `'{m, n}'`|Совпадение от `m` до `n` раз ---- ### Жадность и лень Каждый квантификатор может быть: * жадный (_англ._ greedy): находит как можно больше подходящих символов * ленивый (_англ._ lazy): находит как можно меньше подходящих символов По умолчанию, все квантификаторы являются жадными; для включения «ленивого» режима необходимо поставить знак `'?'` после квантификатора ---- ### Разница между жадным и ленивым режимом https://regex101.com/r/grLIWd/1 https://regex101.com/r/grLIWd/2 ---- ### Задания https://regex101.com/r/aGn8QC/2 1. Найдите все натуральные числа (возможно, окружённые буквами) 2. Найдите все «слова», написанные капсом (то есть строго заглавными), возможно внутри настоящих слов (ааа<u><font color="#007d5b">БББ</font></u>ввв) 3. Найдите слова, в которых есть русская буква, а когда-нибудь за ней цифра 4. Найдите все слова, начинающиеся с русской или латинской большой буквы (`\b` — граница слова) ---- 5. Найдите слова, которые начинаются на гласную (`\b` — граница слова) 6. Найдите все натуральные числа, не находящиеся внутри или на границе слова 7. Найдите строчки, в которых есть символ `*` (`.` — это точно не конец строки!) 8. Найдите строчки, в которых есть открывающая и когда-нибудь потом закрывающая скобки 9. Выделите одним махом весь кусок оглавления (в конце примера, вместе с тегами) 10. Выделите одним махом только текстовую часть оглавления, без тегов --- ## 3. Модуль re ---- ### "Сырые" строки - В коде регулярные выражения обычно записываются в виде «сырых» строк (с префиксом `r`, подавляющих экранирование). - Так, при поиске переноса строки удобнее указать `r"\n"` вместо `"\\n"`, избежав необходимости дублировать экранирующий символ ---- ### Способы работы 1. Если предполагается однократная проверка, достаточно использовать функции модуля `re`. При этом во время вызова функции, произойдет компиляция регулярного выражения и дальнейший поиск соответствий 2. Если проверка будет осуществляется не один раз, эффективным будет предварительно один раз скомпилировать выражение (получив специальный объект класса `re.regex`), а затем использовать его методы ---- ### Функции модуля re Функция|Описание -|- `re.search(pattern,` `string)`|Найти в строке `string` первую строчку, подходящую под шаблон `pattern` `re.fullmatch(pattern,` `string)`|Проверить, подходит ли строка `string` под шаблон `pattern` ---- Функция|Описание -|- `re.split(pattern,` `string,` `maxsplit=0)`|Аналог `str.split()`, только разделение происходит по подстрокам, подходящим под шаблон `pattern` `re.findall(pattern,` `string)`|Найти в строке `string` все непересекающиеся шаблоны `pattern` ---- Функция|Описание -|- `re.finditer(pattern,` `string)`|Итератор всем непересекающимся шаблонам `pattern` в строке `string` (выдаются `match`-объекты) `re.sub(pattern,` `repl,` `string,` `count=0)`|Заменить в строке `string` все непересекающиеся шаблоны `pattern` на `repl` ---- ### Примеры ``` python= import re match = re.search(r'\d\d\D\d\d', r'Телефон 123-12-12') print(match[0] if match else 'Not found') # 23-12 match = re.search(r'\d\d\D\d\d', r'Телефон 1231212') print(match[0] if match else 'Not found') # Not found ``` ---- ``` python= match = re.fullmatch(r'\d\d\D\d\d', r'12-12') print('YES' if match else 'NO') # YES match = re.fullmatch(r'\d\d\D\d\d', r'Т. 12-12') print('YES' if match else 'NO') # NO print(re.split(r'\W+', 'Где, скажите мне, мои очки??!')) # ['Где', 'скажите', 'мне', 'мои', 'очки', ''] ``` ---- ``` python= print(re.findall(r'\d\d\.\d\d\.\d{4}', r'Эта строка написана 19.01.2018, а могла бы и 01.09.2017')) # ['19.01.2018', '01.09.2017'] for m in re.finditer(r'\d\d\.\d\d\.\d{4}', r'Эта строка написана 19.01.2018, а могла бы и 01.09.2017'): print('Дата', m[0], 'начинается с позиции', m.start()) # Дата 19.01.2018 начинается с позиции 20 # Дата 01.09.2017 начинается с позиции 45 ``` ---- ``` python= print(re.sub(r'\d\d\.\d\d\.\d{4}', r'DD.MM.YYYY', r'Эта строка написана 19.01.2018, а могла бы и 01.09.2017')) # Эта строка написана DD.MM.YYYY, а могла бы и DD.MM.YYYY ``` ---- ### Дополнительные флаги Константа|Назначение -|- `re.ASCII`| По умолчанию `\w`, `\W`, `\b`, `\B`, `\d`, `\D`, `\s`, `\S` соответствуют все юникодные символы с соответствующим качеством.<br> `re.ASCII` ускоряет работу, если все соответствия лежат внутри ASCII ---- Константа|Назначение -|- `re.IGNORECASE`| Не различать заглавные и маленькие буквы. Работает медленнее, но иногда удобно `re.MULTILINE`| Специальные символы `^` и `$` соответствуют началу и концу каждой строки `re.DOTALL`| По умолчанию символ `\n` конца строки не подходит под точку. С этим флагом точка — вообще любой символ ---- ### Примеры ``` python= print(re.findall(r'\d+', '12 + ٦٧')) # ['12', '٦٧'] print(re.findall(r'\w+', 'Hello, мир!')) # ['Hello', 'мир'] print(re.findall(r'\d+', '12 + ٦٧', flags=re.ASCII)) # ['12'] print(re.findall(r'\w+', 'Hello, мир!', flags=re.ASCII)) # ['Hello'] print(re.findall(r'[уеыаоэяию]+', 'ОООО ааааа ррррр ЫЫЫЫ яяяя')) # ['ааааа', 'яяяя'] print(re.findall(r'[уеыаоэяию]+', 'ОООО ааааа ррррр ЫЫЫЫ яяяя', flags=re.IGNORECASE)) # ['ОООО', 'ааааа', 'ЫЫЫЫ', 'яяяя'] ``` ---- ``` python= text = r""" Торт с вишней1 вишней2 """ print(re.findall(r'Торт.с', text)) # [] print(re.findall(r'Торт.с', text, flags=re.DOTALL)) # ['Торт\nс'] print(re.findall(r'виш\w+', text, flags=re.MULTILINE)) # ['вишней1', 'вишней2'] print(re.findall(r'^виш\w+', text, flags=re.MULTILINE)) # ['вишней2'] ``` ---- ### Компиляция регулярок `re.compile(pattern, flags=0)` : Компилирует регулярное выражение `pattern`, используя флаги `flags` (например, `re.MULTILINE`) и возвращает объект класса `re.regex` *Пример:* ```python= import re regex = re.compile("^.+?(\d)") print(regex.search("Привет1 Привет12")[0]) ``` ---- ### Match-объекты - Если функции `re.search`, `re.fullmatch` не находят соответствие шаблону в строке, то они возвращают `None`, функция `re.finditer` не выдаёт ничего - Если соответствие найдено, то возвращается `match`-объект. Эта штука содержит в себе кучу полезной информации о соответствии шаблону ---- ### Атрибуты match Метод | Описание -|- `match[0]`, `match.group()`|Подстрока, соответствующая шаблону ```python= match = re.search(r'\w+', r'$$ What??') print(match[0]) # 'What' ``` ---- Метод | Описание -|- `match.start()`|Индекс в исходной строке, начиная с которого идёт найденная подстрока `match.end()`|Индекс в исходной строке, который следует сразу за найденной подстрокой ```python= match = re.search(r'\w+', r'$$ What??') print(match.start()) # 3 print(match.end()) # 7 ``` ---- ![](https://i.imgur.com/QH7cMbY.png) --- ## 4. Группировка и выбор ---- ### Группирующие скобки (...) - Если в шаблоне регулярного выражения встречаются скобки `(...)` без `?:`, то они становятся _группирующими_ - В match-объекте, который возвращают `re.search`, `re.fullmatch` и `re.finditer`, по каждой такой группе можно получить ту же информацию, что и по всему шаблону: - часть подстроки, которая соответствует `(...)` - индексы начала и окончания в исходной строке ---- ### Пример с группами ```python= import re pattern = r'\s*([А-Яа-яЁё]+)(\d+)\s*' string = r'--- Опять45 ---' match = re.search(pattern, string) print(f'Найдена строка >{match[0]}< с позиции {match.start(0)}', f'до {match.end(0)}') print(f'Группа букв >{match[1]}< с позиции {match.start(1)}', f'до {match.end(1)}') print(f'Группа цифр >{match[2]}< с позиции {match.start(2)}', f'до {match.end(2)}') ``` ``` Найдена строка > Опять45 < с позиции 3 до 16 Группа букв >Опять< с позиции 6 до 11 Группа цифр >45< с позиции 11 до 13 ``` ---- ![](https://i.imgur.com/gzhZSyf.png) ![](https://i.imgur.com/OY5Oa7p.png) ---- ### Особенности групп с квантификатором - Если к группирующим скобкам применён квантификатор (то есть указано число повторений), то подгруппа в `match`-объекте будет создана только для последнего соответствия ---- - Например, если бы в для строки <br>`'--- Опять45 ---'` <br>квантификаторы были снаружи от скобок <br>`'\s*([А-Яа-яЁё])+(\d)+\s*'`, то вывод был бы таким: ``` Найдена подстрока > Опять45 < с позиции 3 до 16 Группа букв >ь< с позиции 10 до 11 Группа цифр >5< с позиции 12 до 13 ``` ---- ### Вложенные скобки - Внутри группирующих скобок могут быть и другие группирующие скобки. - В этом случае их нумерация производится в соответствии с номером появления открывающей скобки в шаблоне ---- ```python= import re pattern = r'((\d)(\d))((\d)(\d))' string = r'123456789' match = re.search(pattern, string) print(f'Найдена строка >{match[0]}< с позиции {match.start(0)}', f'до {match.end(0)}') for i in range(1, 7): print(f'Группа №{i} >{match[i]}< с позиции {match.start(i)}', f'до {match.end(i)}') ``` ``` Найдена строка >1234< с позиции 0 до 4 Группа №1 >12< с позиции 0 до 2 Группа №2 >1< с позиции 0 до 1 Группа №3 >2< с позиции 1 до 2 Группа №4 >34< с позиции 2 до 4 Группа №5 >3< с позиции 2 до 3 Группа №6 >4< с позиции 3 до 4 ``` ---- ### Использование групп при заменах - Использование групп добавляет замене (`re.sub`) очень удобную возможность: в шаблоне для замены можно ссылаться на соответствующую группу при помощи `\1, \2, \3, ...` ---- - Например, если нужно даты из неудобного формата `ММ/ДД/ГГГГ` перевести в удобный `ДД.ММ.ГГГГ`, то можно использовать такую регулярку: ```python= import re text = """ We arrive on 03/25/2018. So you are welcome after 04/01/2018. """ print(re.sub(r'(\d\d)/(\d\d)/(\d{4})', r'\2.\1.\3', text)) ``` ``` We arrive on 25.03.2018. So you are welcome after 01.04.2018. ``` ---- ### Невыделяемая группа `(?:часть_выражения)` : Определяет невыделяемую группу (без захвата) *Пример:* * выражение `'(?:\w+)\s'` выполнит в тексте поиск слов, отделенных пробельным символом; при этом слово НЕ будет захвачено в отдельную группу ---- ### Выбор Операция выбора позволяет захватить одно из нескольких выражений в качестве результата поиска `выражение1|выражение2|выражение3` : Соответствует любому элементу, разделенному вертикальной чертой `'|'` ---- ### Пример * выражение `'красн(?:ый|оватый|енький)'` найдет в тексте слова `'красный'`, `'красноватый'`, `'красненький'`; при этом окончание не будет захвачено в группу --- ## 5. Проверка границ (привязки) ---- ### Простые шаблоны Шаблон | Описание | Пример | Результат -|-|-|- `^`|Начало всего текста или начало строчки текста, если `flag=re.MULTILINE`|`^Привет`| `$`|Конец всего текста или конец строчки текста, если `flag=re.MULTILINE`|`Будь здоров!$`| ---- Шаблон | Описание | Пример | Результат -|-|-|- `\A`|Строго начало всего текста| | `\Z`|Строго конец всего текста | | `\b`|Начало или конец слова (слева пусто или не-буква, справа буква и наоборот)|`\bвал`|<u>вал</u>, перевал, Перевалка ---- Шаблон | Описание | Пример | Результат -|-|-|-- `\B`|Не граница слова: либо и слева, и справа буквы, либо и слева, и справа НЕ буквы|`\Bвал`| пере<u>вал</u>, вал, Пере<u>вал</u>ка| &nbsp;|&nbsp;|`\Bвал\B`|перевал, вал, Пере<u>вал</u>ка ---- ![](https://i.imgur.com/wwq2M6D.png) ---- ### Сложные шаблоны (_lookaround_) Шаблон|Описание|Пример|Результат -|-|-|- `(?=...)`|_lookahead assertion_, соответствует каждой позиции, сразу после которой начинается соответствие шаблону `...`|`Isaac (?=Asimov)`|<u>Isaac </u>Asimov, Isaac other ---- Шаблон|Описание|Пример|Результат -|-|-|- `(?!...)`|_negative lookahead assertion_, соответствует каждой позиции, сразу после которой НЕ может начинаться шаблон `...`|`Isaac (?!Asimov)`|Isaac Asimov, <u>Isaac </u>other ---- Шаблон|Описание|Пример|Результат -|-|-|- `(?<=...)`|_positive lookbehind assertion_, соответствует каждой позиции, которой может заканчиваться шаблон `...`<br>Длина шаблона должна быть фиксированной|`(?<=abc)def`|abc<u>def</u>, bcdef ---- Шаблон|Описание|Пример|Результат -|-|-|- `(?<!...)`|_negative lookbehind assertion_, соответствует каждой позиции, которой НЕ может заканчиваться шаблон `...`|`(?<!abc)def`|abcdef, bc<u>def</u> ---- ![](https://i.imgur.com/7GQu8Zm.png) ---- ### Примеры Шаблон | Комментарий | Результат -|-|- `(?<!\d)\d(?!\d)`|Цифра, окружённая не-цифрами|Text ABC 123 A<u>1</u>B<u>2</u>C<u>3</u>! `(?<=#START#).*?(?=#END#)`|Текст от `#START#` до `#END#`|text from #START#<u> till </u>#END# ---- Шаблон | Комментарий | Результат -|-|- `\d+(?=_(?!_))`|Цифра, после которой идёт ровно одно подчёркивание|<u>12</u>_34__56 `^(?:(?!boo).)*?$`|Строка, в которой нет boo (то есть нет такого символа, перед которым есть boo)| <u>a foo and</u><br>boo and zoo>br><u>and others</u> ---- Шаблон | Комментарий | Результат -|-|- `^(?:(?!boo)(?!foo).)*?$`|Строка, в которой нет ни boo, ни foo|a foo and<br>boo and zoo<br><u>and others</u> --- ## Спасибо за внимание! ![](https://i.imgur.com/gKDsna4.png) (c) Яценко Р.Н., 2019-2021 [Учебный центр компьютерных технологий "Кит"](http://kit.kh.ua/)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully