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
      • Invitee
    • 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
    • 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 Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync 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
Invitee
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
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
--- 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