Regular Expressions 101

Save & Share

Flavor

  • PCRE2 (PHP >=7.3)
  • PCRE (PHP <7.3)
  • ECMAScript (JavaScript)
  • Python
  • Golang
  • Java 8
  • .NET 7.0 (C#)
  • Rust
  • Regex Flavor Guide

Function

  • Match
  • Substitution
  • List
  • Unit Tests

Tools

Sponsors
There are currently no sponsors. Become a sponsor today!
An explanation of your regex will be automatically generated as you type.
Detailed match information will be displayed here automatically.
  • All Tokens
  • Common Tokens
  • General Tokens
  • Anchors
  • Meta Sequences
  • Quantifiers
  • Group Constructs
  • Character Classes
  • Flags/Modifiers
  • Substitution
  • A single character of: a, b or c
    [abc]
  • A character except: a, b or c
    [^abc]
  • A character in the range: a-z
    [a-z]
  • A character not in the range: a-z
    [^a-z]
  • A character in the range: a-z or A-Z
    [a-zA-Z]
  • Any single character
    .
  • Alternate - match either a or b
    a|b
  • Any whitespace character
    \s
  • Any non-whitespace character
    \S
  • Any digit
    \d
  • Any non-digit
    \D
  • Any word character
    \w
  • Any non-word character
    \W
  • Non-capturing group
    (?:...)
  • Capturing group
    (...)
  • Zero or one of a
    a?
  • Zero or more of a
    a*
  • One or more of a
    a+
  • Exactly 3 of a
    a{3}
  • 3 or more of a
    a{3,}
  • Between 3 and 6 of a
    a{3,6}
  • Start of string
    ^
  • End of string
    $
  • A word boundary
    \b
  • Non-word boundary
    \B

Regular Expression
No Match

/
/
gm

Test String

Substitution

Processing...

Code Generator

Generated Code

re = /\/\*\*?\s*([\W\w]*?)\s*\*\/|\/\/\s*(.*)$/m str = '/** * The Crypto Info Bot * * @author Denis Efremov <efremov.a.denis@gmail.com> */ require(\'dotenv\').load() const axios = require(\'axios\') const winston = require(\'winston\') const { inspect } = require(\'util\') const Telegraf = require(\'telegraf\') const { session } = Telegraf const { API_URL, BOT_TOKEN, BOT_USERNAME, PAGE_SIZE, FIXED_LENGTH, } = process.env /** * Debug helper * * @param {Mixed} data The data * @return {Mixed} */ const debug = (data) => console.log(inspect(data, { showHidden: true, colors: true, depth: 10, })) const logger = winston.createLogger({ level: \'info\', format: winston.format.json(), transports: [ new winston.transports.File({ filename: \'log/error.log\', level: \'error\' }), new winston.transports.File({ filename: \'log/combined.log\' }), ], }) const formattedTime = (date) => date.toTimeString() const formattedDate = (date) => date.toDateString() const formattedDateTime = (date) => \\`${formattedDate(date)} ${formattedTime(date)}\\` /** * Create a new bot instance * * @type {Telegraf} */ const bot = new Telegraf(BOT_TOKEN, { username: BOT_USERNAME }) bot.use(session()) /** * Render pagination buttons * * @param {String} ns The namespace * @param {Number} page The page * @param {Number} total The total * @return {Object} Message parameters */ const pagination = (namespace, page, total) => ({ reply_markup: { inline_keyboard: [[ page !== 0 ? { text: `< Prev ${PAGE_SIZE}`, callback_data: `/${namespace}/prev` } : { text: \'----------\', callback_data: \'/noop\' }, { text: `${page * PAGE_SIZE} - ${(page + 1) * PAGE_SIZE} (${total})`, callback_data: \'/noop\', }, page !== (total / PAGE_SIZE) - 1 ? { text: `Next ${PAGE_SIZE} >`, callback_data: `/${namespace}/next` } : { text: \'----------\', callback_data: \'/noop\' }, ]], }, }) /** * Gets the rates. * * @param {Number} start The start * @param {Number} limit The limit * @return {Promise} The rates */ const getRates = (start, limit) => { let url = `${API_URL}?convert=RUB` if (limit) { url += `&limit=${limit}` } if (start) { url += `&start=${start}` } return axios.get(url) } /** * Gets the rate. * * @param {String} asset The asset * @return {Promise} The rate */ const getRate = (asset) => axios.get(`${API_URL}${asset}/?convert=RUB`) /** * A currency message template * * @param {Object} rate The rate object * @param {String} rate.name The name * @param {String} rate.symbol The symbol * @param {Number} rate.price_usd The price usd * @param {Number} rate.price_rub The price rub * @param {Number} rate.percent_change_1h:hour The percent change 1 h hour * @param {Number} rate.percent_change_24h:day The percent change 24 h day * @param {Number} rate.percent_change_7d:week The percent change 7 d week * @return {String} */ const templateMd = ({ name, symbol, price_usd, price_rub, percent_change_1h: hour, percent_change_24h: day, percent_change_7d: week, }) => `${name} *(${symbol})* \\`!${symbol.toLowerCase()}\\` \\`\\`\\` ================== $ ${price_usd} ₽ ${price_rub} ================== ${hour > 0 ? \'+\' : \'\'}${parseFloat(hour).toFixed(FIXED_LENGTH)}% / 1h ${day > 0 ? \'+\' : \'\'}${parseFloat(day).toFixed(FIXED_LENGTH)}% / 24h ${week > 0 ? \'+\' : \'\'}${parseFloat(week).toFixed(FIXED_LENGTH)}% / 7d \\`\\`\\`` /** * A currency message small template * * @param {Object} rate The rate object * @param {String} rate.name The name * @param {String} rate.symbol The symbol * @param {Number} rate.price_usd The price usd * @param {Number} rate.price_rub The price rub * @return {String} */ const smallTemplateMd = ({ name, symbol, price_usd, price_rub }) => ` ${name} *(${symbol})* \\`!${symbol.toLowerCase()}\\` \\`\\`\\` $ ${price_usd} | ₽ ${price_rub} \\`\\`\\`` /** * Map command listaners * * @param {Object[]} rates The rates * @return {Promise} */ const mapCommands = async (/*rates*/) => rates.reduce((acc, rate) => { const command = rate.symbol.toLowerCase() bot.hears(`!${command}`, async (ctx) => { let intervalId let response = await getRate(ctx.index[command]).catch((error) => { debug(error) clearInterval(intervalId) }) let text = templateMd(response.data[0]) let message = await ctx.replyWithMarkdown( `${text} Updated: ${formattedTime(new Date()).slice(0, 8)}` ).catch((error) => { debug(error) clearInterval(intervalId) }) message.text = text intervalId = setInterval(async () => { response = await getRate(ctx.index[command]).catch((error) => { debug(error) clearInterval(intervalId) }) text = templateMd(response.data[0]) if (text === message.text) { return } message = await ctx.tg.editMessageText( ctx.chat.id, message.message_id, undefined, `${text} Updated: ${formattedTime(new Date()).slice(0, 8)}`, { parse_mode: \'Markdown\' } ).catch((error) => { debug(error) clearInterval(intervalId) }) message.text = text }, 5000) }) acc[command] = rate.id return acc }, {}) bot.use((ctx, next) => { logger.log({ level: \'info\', message: ctx.message }) debug(ctx.message) next() }) /** * The rates command * * @param {TelegrafContext} ctx The bot\'s context */ bot.hears(\'!rates\', async (ctx) => { ctx.session.ratesPage = ctx.session.ratesPage || 0 const { data } = await getRates( ctx.session.ratesPage * PAGE_SIZE, (ctx.session.ratesPage * PAGE_SIZE) + PAGE_SIZE ) await ctx.replyWithMarkdown( data.map(smallTemplateMd).join(\'\'), pagination(\'rates\', ctx.session.ratesPage, Object.keys(ctx.index).length), ).catch(debug) }) /** * The time command * * @param {TelegrafContext} ctx The bot\'s context */ bot.hears(\'!time\', async (ctx) => { const message = await ctx.replyWithMarkdown(formattedDateTime(new Date())) .catch(debug) const intervalId = setInterval(async () => { await ctx.tg.editMessageText( ctx.chat.id, message.message_id, undefined, formattedDateTime(new Date()) ).catch((error) => { debug(error) clearInterval(intervalId) }) }, 3000) }) /** * The currencies list command * * @param {TelegrafContext} ctx The bot\'s context */ bot.hears(\'!list\', async (ctx) => { const text = Object.keys(ctx.index) .map((key) => ` ${ctx.index[key]} \\`!${key}\\``) .join(\'\') await ctx.replyWithMarkdown(text).catch(debug) }) /** * Change page action * * @param {TelegrafContext} ctx The bot\'s context */ bot.action(/^/rates/(w+)$/, async (ctx) => { const current = ctx.session.ratesPage || 0 const allKeys = Object.keys(ctx.index) // klskdlskakdsl switch (ctx.match[1]) { case \'prev\': ctx.session.ratesPage = current > 0 ? current - 1 // slklklkl : 0 break case \'next\': ctx.session.ratesPage = current < (allKeys.length / PAGE_SIZE) - 1 ? current + 1 : (allKeys.length / PAGE_SIZE) - 1 break default: } const { data } = await getRates(ctx.session.ratesPage * PAGE_SIZE, PAGE_SIZE) .catch(debug) await ctx.editMessageText( data.map(smallTemplateMd).join(\'\'), { disable_web_page_preview: true, parse_mode: \'Markdown\', ...pagination(\'rates\', ctx.session.ratesPage, allKeys.length), } ).catch(debug) return ctx.answerCbQuery() }) /** * Handles noop actions * * @param {TelegrafContext} ctx The bot\'s context */ bot.action(/^/noop$/, async (ctx) => ctx.answerCbQuery()) /** * Handles the help command */ bot.command(\'help\', async (ctx) => { const message = await ctx.replyWithMarkdown(` *Usage:* \\`!list\\` - List of all supported currencies without rates \\`!rates\\` - Paginated list of all supported currencies with rates \\`!{TICKER}\\` - Show rate of exact currency by its ticker `) if (message) { setTimeout(() => { ctx.deleteMessage(message.message_id) }, 7000) } }) // bot.command(\'leave\', async (ctx) => { // ctx.tg.leaveChat(ctx.chat.id) // }) // bot.on(\'inline_query\', async (ctx) => { // }) /** * Init the bot * * @param {TelegrafContext} ctx The bot\'s context */ const run = async (instance) => { const { data } = await getRates().catch(console.log) instance.context.index = await mapCommands(data) return instance } /** * Start the bot * * @param {Telegraf} instance The bot instance */ run(bot).then((instance) => { instance.startPolling() })' subst = '' result = str.gsub(re, subst) # Print the result of the substitution puts result

Please keep in mind that these code samples are automatically generated and are not guaranteed to work. If you find any syntax errors, feel free to submit a bug report. For a full regex reference for Ruby, please visit: http://ruby-doc.org/core-2.2.0/Regexp.html