<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Tasnim Ferdous - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Tasnim Ferdous - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 08 May 2026 11:09:40 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/renzhamin/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Use Tampermonkey to Improve a Website's UI – Example Using freeCodeCamp ]]>
                </title>
                <description>
                    <![CDATA[ What is Tampermonkey? Tampermonkey is a browser extension that lets you add custom scripts to websites, making them work or look the way you want. It's like giving websites a makeover or adding new features.  These scripts are called userscripts and ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/customize-website-experience-with-tampermonkey/</link>
                <guid isPermaLink="false">66ba2e3eab41bfc0b9b131e2</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tasnim Ferdous ]]>
                </dc:creator>
                <pubDate>Tue, 22 Aug 2023 22:32:35 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/cover_image.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-what-is-tampermonkey">What is Tampermonkey?</h2>
<p>Tampermonkey is a browser extension that lets you add custom scripts to websites, making them work or look the way you want. It's like giving websites a makeover or adding new features. </p>
<p>These scripts are called userscripts and you can make tampermonkey run those scripts when you visit a particular site.</p>
<h2 id="heading-how-to-use-tampermonkey-to-customize-the-user-experience-of-a-website">How to Use Tampermonkey to Customize the User Experience of a Website</h2>
<p>There are a lot of use cases for Tampermonkey. The most obvious one is adding your own custom styling. You can add custom css for a specific site and change up the appearance as you want. But as you can run a script, you can also manipulate the DOM elements. </p>
<p>I will list out of the things I have done to give you some ideas to what is possible.</p>
<ol>
<li>Increase readability by changing font properties.</li>
<li>Remove ads on sites that don't allow an ad blocker.</li>
<li>Declutter a site so that you can focus on the portion that you're interested in.</li>
<li>Add keyboard shortcuts for repetitive tasks.</li>
<li>Add buttons for custom actions.</li>
<li>Automatically fill out form data.</li>
</ol>
<p>Today you'll get a glimpse of what you can do with Tampermonkey by writing scripts that'll work on the freeCodeCamp /news site. </p>
<p>First we will see how to declutter for a more focused reading experience. Then we will attach copy buttons on the code snippets. And lastly, we'll automatically generate table of contents that you can access with a toggle button. </p>
<p>The source code is available on <a href="https://github.com/renzhamin/freecodecamp-enhancer" target="_blank">GitHub</a>.</p>
<p>It's worth pointing out that any changes your script makes will only be available in your browser. So as long as you don't deal with any sensetive data on that site, you can go as wild as you want. </p>
<p>But be aware that some sites may have some policy in regards to using third party JavaScript and take disciplinary action if you violate that policy.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>As we will be modifying a website, basic knowledge of HTML, CSS, and JavaScript is required to go through this tutorial. Some experience with DOM manipulation would be great as well. </p>
<p>If you can effectively manipulate exisiting DOM elements, that will allow you to write Tampermonkey scripts to make yourself more productive on any website. </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#how-to-install-tampermonkey">How to Install Tampermonkey</a></li>
<li><a class="post-section-overview" href="#how-to-create-a-new-userscript">How to Create a New Userscript</a></li>
<li><a class="post-section-overview" href="#how-to-change-a-buttons-behaviour">How to Change a Button's Behaviour</a></li>
<li><a class="post-section-overview" href="#how-to-implement-reader-view-for-freecodecamp-news-articles">How to Implement Reader View for freeCodeCamp /news Articles</a></li>
<li><a class="post-section-overview" href="#how-to-add-copy-buttons-to-the-code-snippets">How to Add Copy Buttons to the Code Snippets</a></li>
<li><a class="post-section-overview" href="#how-to-auto-generate-a-table-of-contents">How to Auto-generate a Table of Contents</a></li>
<li><a class="post-section-overview" href="#key-takeways">Key Takeways</a></li>
</ul>
<p><a></a></p>
<h2 id="heading-how-to-install-tampermonkey">How to Install Tampermonkey</h2>
<p>For Chrome, the extension is available on the <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo" target="_blank">chrome web store.</a></p>
<p>It's also available for Firefox, which you can install from <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Firefox Add-Ons.</a></p>
<p>For other browsers, you can visit the <a href="https://www.tampermonkey.net/index.php" target="_blank">Tampermonkey Home Page</a>. Currently, Chrome, Firefox, Edge, Safari and Opera are officially supported. But the one from Chrome web store works fine on Chromium-based browsers like Brave.</p>
<p><a></a></p>
<h2 id="heading-how-to-create-a-new-userscript">How to Create a New Userscript</h2>
<p>The easiest way to get started is using the <code>create new script</code> option from the toolbar.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/create-new-script.jpeg" alt="create new script" width="600" height="400" loading="lazy"></p>
<p>You will be presented with something like this:</p>
<pre><code><span class="hljs-comment">// ==UserScript==</span>
<span class="hljs-comment">// @name         New Userscript</span>
<span class="hljs-comment">// @namespace    http://tampermonkey.net/</span>
<span class="hljs-comment">// @version      0.1</span>
<span class="hljs-comment">// @description  try to take over the world!</span>
<span class="hljs-comment">// @author       You</span>
<span class="hljs-comment">// @match        https://www.freecodecamp.org/news/*</span>
<span class="hljs-comment">// @icon         https://www.google.com/s2/favicons?sz=64&amp;domain=freecodecamp.org</span>
<span class="hljs-comment">// @grant        none</span>
<span class="hljs-comment">// ==/UserScript==</span>

(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-meta">    'use strict'</span>;

    <span class="hljs-comment">// Your code here...</span>
})();
</code></pre><p>The userscript header contains information about the script and other important parameters as well. </p>
<p>For now the relevant part is <code>@match</code>. This tells Tampermonkey for which sites to run the script. I have changed to match any article on the freeCodeCamp news site by using the "*" wildcard.</p>
<p>Test it out by putting something simple as <code>alert("HI")</code> on the function. Then navigate to any article on freecodecamp.org/news. </p>
<p>Lets do something interesting next. This will be a brief introduction to DOM manipulation. </p>
<p>Before writing code in the userscript, it's better to write out your code in the dev console first. Then, when you have working code, you can just paste it in the Tampermonkey script. This is how we will write all the scripts in this article as well.</p>
<p><a></a></p>
<h2 id="heading-how-to-change-a-buttons-behaviour">How to Change a Button's Behaviour</h2>
<p>At the end of each freeCodeCamp article there is a button that says "Tweet a thanks". You can see the onclick function of the button by using the element picker from the dev tools and clicking on that button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/tweet-btn.jpeg" width="600" height="400" alt="tweet-btn" loading="lazy"></p>
<p>Clicking on that button will open a new window in Twitter with some default text for a tweet like the following:</p>
<pre><code>Thank you @twitter-username-<span class="hljs-keyword">of</span>-author <span class="hljs-keyword">for</span> writing <span class="hljs-built_in">this</span> helpful article.

Title <span class="hljs-keyword">of</span> Article
<span class="hljs-attr">https</span>:<span class="hljs-comment">//www.freecodecamp.org/news/slug-of-article</span>
</code></pre><p>Let's say that you want to change up the default text to something like this:</p>
<pre><code>This article is quite fascinating.

Title <span class="hljs-keyword">of</span> Article by @twitter-username-<span class="hljs-keyword">of</span>-author
<span class="hljs-attr">https</span>:<span class="hljs-comment">//www.freecodecamp.org/news/slug-of-article</span>
</code></pre><p>The new text is static as you will chose it, but there are some variables here. You need to extract these 3 things:</p>
<ol>
<li>The link of the article.</li>
<li>The Twitter username of the author.</li>
<li>Title of the article.</li>
</ol>
<p>Extracting the link is as <code>location.href</code>.</p>
<p>Now how to extract the Twitter username?</p>
<p>At first glance, you may think the name after the author image is the answer. But that's actually the author's name (which is not necessarily unique) – it's not their Twitter username. So, where would you start looking for it?</p>
<p>Since you can just click the button or inspect it, you already know the values of all these variables. So a good way to find the Twitter username would be just to search for it in the entire HTML document. As it is unique, there won't be many occurrences. </p>
<p>Open the dev tools and search for the Twitter username. You will find this tag:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"twitter:creator"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"@username"</span> /&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/twitter-username-search.jpeg" width="600" height="400" alt="twitter-username-search" loading="lazy"></p>
<p>Which is not surprising, as most publishing sites put information such article title, tags, description, and author information in the meta tags for SEO purposes. But most of the time you can just find what you are looking for by using the inspection tool.</p>
<p>So how do we extract the "content" property of this meta tag? Considering that an article is written by only one author, there will always be only one meta tag with name "twitter:creator". So you can just use the querySelector.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> username = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'meta[name="twitter:creator"]'</span>).content
</code></pre>
<p>Now on to extracting the title. If you have wandered around looking for the Twitter username, you will find that there is also a meta tag with name "twitter:title". But this time, the title is something that you can visually see and inspect. In most cases that's the easier way to go about it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/article-title.png" width="600" height="400" alt="article-title" loading="lazy"></p>
<p>If you inspect on the article title above the cover image, you will see it's inside an h1 with the class "post-full-title". You can select this with the following:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> title = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"h1.post-full-title"</span>).textContent
</code></pre>
<p>Now we have all the pieces we need to make the change. Inspect the target button and you will see it comes with an id of "tweet-btn". Now let's define our text.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> text = <span class="hljs-string">`This article is quite fascinating.

<span class="hljs-subst">${title}</span> by <span class="hljs-subst">${username}</span>
<span class="hljs-subst">${location.href}</span>
`</span>
</code></pre>
<p>Putting it all together by changing the button's onclick, here's what you should have:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">change_tweet_text</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"tweet-btn"</span>).onclick = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> username = <span class="hljs-built_in">document</span>.querySelector(
            <span class="hljs-string">'meta[name="twitter:creator"]'</span>
        ).content
        <span class="hljs-keyword">const</span> title = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"h1.post-full-title"</span>).textContent

        <span class="hljs-keyword">const</span> text = <span class="hljs-string">`This article is quite fascinating.

<span class="hljs-subst">${title}</span> by <span class="hljs-subst">${username}</span>
<span class="hljs-subst">${location.href}</span>
`</span>

        <span class="hljs-keyword">const</span> share_link = <span class="hljs-built_in">encodeURI</span>(
            <span class="hljs-string">`https://twitter.com/intent/tweet?text=<span class="hljs-subst">${text}</span>`</span>
        )

        <span class="hljs-built_in">window</span>.open(share_link, <span class="hljs-string">"share-twitter"</span>, <span class="hljs-string">"width=550, height=235"</span>)

        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
    }
}
</code></pre>
<p>Because the text contains newline characters, we have to convert it url-encoded format.</p>
<p>Paste the function inside the userscript and call it.</p>
<pre><code class="lang-js">;(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    change_tweet_text()
})()
</code></pre>
<p>Now reload the article page and click on the button to see if it's working as expected.</p>
<p>It is possible that it won't work as expected. Especially if the page takes a lot of time to load. </p>
<p>With this script, we are modifying a button which is a DOM element. But what if the element is not yet loaded? Then you will face unexpected results. </p>
<p>Tampermonkey provides the "// @run-at" header which you can specify to "document-end", but I've found that it still produces unexpected results sometimes. </p>
<p>The right way to mitigate it is to use the "load" event which is emitted when the whole page is loaded. So, we can refactor our driver function in the following way:</p>
<pre><code class="lang-js">addEventListener(<span class="hljs-string">"load"</span>, main)

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
}
</code></pre>
<p>You can be extra careful and throw a delay as well:</p>
<pre><code class="lang-js">addEventListener(<span class="hljs-string">"load"</span>, <span class="hljs-built_in">setTimeout</span>(main, <span class="hljs-number">2000</span>))
</code></pre>
<p>From now on, we will call all the functions in this main function which will run after the load event is fired.</p>
<p>Note that you could have achieved the same result using a regular expression. In that case, you could extract the variables from the button's onclick which contains the URL with the full text. </p>
<p>But extracting information from the tags is better because if one day the default tweet text changes, you would need the change the regex as well.</p>
<p><a></a></p>
<h2 id="heading-how-to-implement-reader-view-for-freecodecamp-news-articles">How to Implement Reader View for freeCodeCamp /news Articles</h2>
<p>If you make your browser fullscreen, the top navigation bar still stays at the top – which is not the best reading experience. So, we want to hide everything except the main article body. </p>
<p>Let's start inspecting. You will see that, close to the "main" tag, there is an "article" tag with class "post". It contains all the text of the article including the heading and cover image.</p>
<p>This time, we will live-edit the CSS of the page. In Firefox, you can use the "Style Editor" tab in the dev tools. Click on the "+" icon and start testing out your CSS.</p>
<p>As of now, there is no built-in way to hide all elements except one (in our case article.post) using CSS. You might be tempted to use <code>:not(article.post)</code> but it won't work because if any ancestor of the element is hidden, then all the descendents will be hidden as well. </p>
<p>We can accomplish our goal by using the ":has" selector. <code>:has(article.post)</code> will select all the ancestors of article.post so we can select the inverse of it with <code>:not(:has(article.post))</code>. </p>
<p>But there's still a problem: the descendent of article.post is also ignored by this selector. We can bring them back by chaining another not –<code>:not(article.post, article.post *)</code>. This will select everything that is not an article.post or a descendent of it.</p>
<p>This is the final CSS:</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:not(</span><span class="hljs-selector-pseudo">:has(article.post))</span><span class="hljs-selector-pseudo">:not(article.post</span>, <span class="hljs-selector-tag">article</span><span class="hljs-selector-class">.post</span> *) {
    <span class="hljs-attribute">display</span>: none;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/style-editor.png" width="600" height="400" alt="style-editor" loading="lazy"></p>
<p>At the time of writing this, the ":has" selector is experimental in Firefox. But you can go to <code>about:config</code> and enable it by changing the value of <code>layout.css.has-selector.enabled</code> to true.</p>
<p>What if you want to use the navigation bar? It would be a shame to manually write the CSS and remove it when you don't want it, right? Especially now that you can do some scripting.</p>
<p>We will add a keyboard shortcut to the site that will make the browser fullscreen. We'll also apply that CSS and, upon exiting fullscreen, the CSS will be removed.</p>
<p>One of the exposed APIs of tampermonkey is <code>GM_addStyle(css)</code> for adding CSS. For this API to work, you need to include <code>// @grant           GM_addStyle</code> to the userscript header and also remove <code>// @grant none</code> if you have that.</p>
<p>To create the shortcut, you will use a "keydown" event listener.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add_declutter_toggle</span>(<span class="hljs-params">key</span>) </span>{
    <span class="hljs-keyword">const</span> css = <span class="hljs-string">`
        .declutter :not(:has(article.post)):not(article.post, article.post *) {
            display: none;
        }`</span>

    GM_addStyle(css)

    <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (event.key === key) {
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">document</span>.fullscreen) {
                <span class="hljs-built_in">document</span>.exitFullscreen()
                <span class="hljs-built_in">document</span>.body.classList.remove(<span class="hljs-string">"declutter"</span>)
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">document</span>.body.classList.add(<span class="hljs-string">"declutter"</span>)
                <span class="hljs-built_in">document</span>.documentElement.requestFullscreen()
            }
        }
    })
}
</code></pre>
<p>We are applying the CSS to the elements with the class "declutter". By toggling the "declutter" on the body we are essentially toggling the CSS on the entire page.</p>
<p>Now, we just have to add the function call in our main function.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
    add_declutter_toggle(<span class="hljs-string">"F"</span>)
}
</code></pre>
<p>Reload the page and you will see that pressing "F" will toggle fullscreen and also remove everything except article body when in the fullscreen state.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/declutter_demo.gif" alt="declutter demo" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h2 id="heading-how-to-add-copy-buttons-to-the-code-snippets">How to Add Copy Buttons to the Code Snippets</h2>
<p>The first step is always identifying the correct selector. If you inspect any code snippet you will see that they're contained in <code>&lt;code&gt;</code> blocks and have a <code>&lt;pre&gt;</code> as a parent. So, to select all code snippets, the selector can be <code>pre &gt; code</code>. </p>
<p>Now the question is where to place the button? For easy access, we want the button to be on top of the code snippet and aligned to the right. You may think, placing it between the <code>&lt;pre&gt;</code> tag and <code>&lt;code&gt;</code> tag
could work – but that will interfere with the content of the code snippet which is not ideal.</p>
<p>The ideal solution is wrapping the <code>&lt;pre&gt;</code> tag with a div and creating the structure as <code>div &gt; button &gt; pre &gt; code</code>.</p>
<p>The copying is done by the clipboard API using the <code>navigator.clipboard.writeText</code> method. We will also apply some styling and let the reader know their copying was successful by changing the button text from "copy" to "copied" for a small duration of time.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">attach_code_copy_btn</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> css = <span class="hljs-string">`
        .copy-btn {
            float: right;
            margin-bottom: 5px;
            border-radius: 1rem;
            font-size: 0.8em;
            width: 7rem;
        }

        .pre-wrapper {
            width: 100%;
        }
    `</span>
    GM_addStyle(css)

    <span class="hljs-keyword">const</span> codes = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">"pre &gt; code"</span>)
    codes.forEach(<span class="hljs-function">(<span class="hljs-params">code</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> pre = code.parentElement
        <span class="hljs-keyword">const</span> btn = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"button"</span>)
        btn.textContent = <span class="hljs-string">"copy"</span>
        btn.classList.add(<span class="hljs-string">"copy-btn"</span>)
        btn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span>
            navigator.clipboard.writeText(code.textContent).then(<span class="hljs-function">() =&gt;</span> {
                btn.textContent = <span class="hljs-string">"copied"</span>
                <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> (btn.textContent = <span class="hljs-string">"copy"</span>), <span class="hljs-number">2500</span>)
            })
        )
        <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>)
        div.classList.add(<span class="hljs-string">"pre-wrapper"</span>)
        div.appendChild(btn)
        wrap(pre, div)
    })
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">wrap</span>(<span class="hljs-params">elem, wrapper</span>) </span>{
    elem.parentNode.insertBefore(wrapper, elem)
    wrapper.appendChild(elem)
}
</code></pre>
<p>The <code>attach_code_copy_btn</code> function finds all code snippets and wraps them around a div, adds classes to them, and attaches a button which will copy the code content on click.</p>
<p>Call the function just as before:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
    add_declutter_toggle(<span class="hljs-string">"F"</span>)
    attach_code_copy_btn()
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/copy_demo.gif" alt="copy demo" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<p>Heres some practice, try adding click event listeners to all <code>&lt;code&gt;</code> tags that are not inside a <code>&lt;pre&gt;</code> tag. On a mouse click on the <code>&lt;code&gt;</code> block the contents of the  block will be copied.</p>
<h2 id="heading-how-to-auto-generate-a-table-of-contents">How to Auto-generate a Table of Contents</h2>
<p>freeCodeCamp is a great place for reading in-depth articles. The articles are well organized as well. Most authors use proper headings and provide a table of contents. But the ToC is not accessible on every part of the page. </p>
<p>If you want to jump somewhere you will have to go back to the ToC then click on your section of interest. And then there are shorter articles that don't provide a ToC at all.</p>
<p>That's where we can introduce a userscript that will automatically generate a table of contents which is accessible anywhere on the page. This improves the user experience when reading longer articles. </p>
<p>The first thing you should know is how a ToC works. A section in the ToC is just an anchor tag with an href value of <code>#some-section</code>. Here "some-section" can be the name property of an anchor tag of the target location (for example, <code>&lt;a name="some-section"&gt;</code>) or if its used as an id on any element (for example, <code>&lt;h2 id="some-section"&gt;</code>).</p>
<p>If you inspect on any heading tag, you will see that the id attribute is present. This is automatically inserted when the article page is built. So we can use that for our ToC.</p>
<p>This is a ToC generator function modified from a <a href="https://stackoverflow.com/questions/187619/is-there-a-javascript-solution-to-generating-a-table-of-contents-for-a-page" target="_blank">StackOverflow answer</a>.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generate_toc</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> toc = <span class="hljs-string">""</span>
    <span class="hljs-keyword">let</span> level = <span class="hljs-number">1</span>

    <span class="hljs-keyword">let</span> container = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".post-content"</span>)
    <span class="hljs-keyword">const</span> regex = <span class="hljs-regexp">/&lt;h([2-5])\s+id="([^"]+)"&gt;([^&lt;]+)&lt;\/h([2-5])&gt;/gi</span>
    <span class="hljs-keyword">const</span> matches = [...container.innerHTML.matchAll(regex)]
    matches.forEach(<span class="hljs-function">(<span class="hljs-params">match</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (match.length != <span class="hljs-number">5</span>) <span class="hljs-keyword">return</span>
        <span class="hljs-keyword">const</span> [_, openLevel, id, titleText, closeLevel] = match

        <span class="hljs-keyword">if</span> (openLevel !== closeLevel) {
            <span class="hljs-keyword">return</span>
        }

        <span class="hljs-keyword">if</span> (openLevel &gt; level) {
            toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(openLevel - level + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;ol&gt;"</span>)
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (openLevel &lt; level) {
            toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(level - openLevel + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;/li&gt;&lt;/ol&gt;"</span>)
        } <span class="hljs-keyword">else</span> {
            toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(level + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;/li&gt;"</span>)
        }

        level = <span class="hljs-built_in">parseInt</span>(openLevel)

        <span class="hljs-keyword">if</span> (!id) {
            id = titleText.replace(<span class="hljs-regexp">/ /g</span>, <span class="hljs-string">"_"</span>)
        }
        toc += <span class="hljs-string">'&lt;li&gt;&lt;a href="#'</span> + id + <span class="hljs-string">'"&gt;'</span> + titleText + <span class="hljs-string">"&lt;/a&gt;&lt;/li&gt;"</span>
    })

    <span class="hljs-keyword">if</span> (level) {
        toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(level + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;/ol&gt;"</span>)
    }

    <span class="hljs-keyword">return</span> toc
}
</code></pre>
<p>We are using a regex to match h2, h3, h4, and h5 tags and creating an ordered list to display to ToC contents and returning the HTML of the whole ToC.</p>
<p>Now we will add this HTML to a div that will contain the ToC. That div will be wrapped inside a dialog component so that we can use a modal to easily show/hide the ToC. We will also add a button that will be used as the toggle.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">show_toc</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">document</span>.documentElement.style.overflow = <span class="hljs-string">"hidden"</span>
    <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"toc-dialog"</span>).showModal()
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add_toc_toggle</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> dialog = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"dialog"</span>)
    dialog.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"toc-dialog"</span>)
    <span class="hljs-keyword">const</span> toc_div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>)
    toc_div.classList.add(<span class="hljs-string">"toc-content"</span>)
    toc_div.innerHTML = <span class="hljs-string">"&lt;h2&gt;Table of Contents&lt;/h2&gt;"</span>
    dialog.appendChild(toc_div)

    <span class="hljs-keyword">const</span> show_toc_btn = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"button"</span>)
    show_toc_btn.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"toc-toggle"</span>)
    show_toc_btn.innerHTML =
        <span class="hljs-string">'&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"&gt;&lt;path d="M.361 256C.361 397 114 511 255 511C397 511 511 397 511 256C511 116 397 2.05 255 2.05C114 2.05 .361 116 .361 256zM192 150V363H149V150H192zM234 150H362V193H234V150zM362 235V278H234V235H362zM234 320H362V363H234V320z"&gt;&lt;/path&gt;&lt;/svg&gt;'</span>

    show_toc_btn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
        show_toc()
    })

    <span class="hljs-comment">// Click anywhere to close</span>
    dialog.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-built_in">document</span>.documentElement.style.overflow = <span class="hljs-string">"auto"</span>
        event.currentTarget.close()
    })

    dialog.addEventListener(<span class="hljs-string">"close"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-built_in">document</span>.documentElement.style.overflow = <span class="hljs-string">"auto"</span>
        event.currentTarget.close()
    })

    <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".post-content"</span>).appendChild(dialog)
    <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".post-content"</span>).appendChild(show_toc_btn)
}
</code></pre>
<p>Finally we will provide a driver function that will perform all the necessary tasks related to ToC generation. We will also add a keyboard shortcut to toggle the ToC.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add_toc</span>(<span class="hljs-params">toc_toggle_key</span>) </span>{
    <span class="hljs-keyword">const</span> css = <span class="hljs-string">`
        .toc-content {
            margin: 0;
            padding: 0;
            max-width: 65vw;
        }

        @media (width &lt;= 800px) {
            .toc-content {
                max-width: 100vw;
            }
        }

        .toc-content h2 {
            padding: 20px 30px;
        }

        .toc-content ol {
            counter-reset: item;
        }

        .toc-content li {
            display: flex;
            white-space: nowrap;
            gap: 5px;
        }

        .toc-content li:before {
            content: counters(item, ".") " ";
            counter-increment: item;
        }

        .toc-content a {
            text-decoration: none;
            white-space: normal;
            flex-grow: 1;
        }

        .toc-content li:hover {
            background: rgba(82, 142, 227, 0.3);
            cursor: pointer;
        }

#toc-toggle {
            background: transparent;
            position: fixed;
            bottom: 5%;
            right: 5%;
            width: 5rem;
        }

#toc-toggle svg {
            fill: rgb(82, 142, 227);
            scale: 1;
        }

#toc-toggle svg:hover {
            scale: 1.15;
        }

#toc-dialog {
            padding: 0;
            border: 0;
        }
`</span>
    GM_addStyle(css)

    add_toc_toggle()
    <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".toc-content"</span>).innerHTML += generate_toc()

    <span class="hljs-keyword">if</span> (toc_toggle_key) {
        <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
            <span class="hljs-keyword">if</span> (event.key === toc_toggle_key) {
                show_toc()
            }
        })
    }
}
</code></pre>
<p>Now, just call the driver function in main.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
    add_declutter_toggle(<span class="hljs-string">"F"</span>)
    attach_code_copy_btn()
    add_toc(<span class="hljs-string">";"</span>)
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/toc_demo-1.gif" alt="toc demo" width="600" height="400" loading="lazy"></p>
<p>Now whenever, you read an article on freeCodeCamp, you can use the toggle button or a shortcut to view the ToC.</p>
<p>For practice, try making the toc sticky in the side (left/right). If you are on a wider screen, having the toc on the side will make the reading experience even better. </p>
<p><a></a></p>
<h2 id="heading-key-takeways">Key Takeways</h2>
<p>You should never run arbitrary JavaScript on any occasion. That goes for Tampermonkey scripts as well. </p>
<p>I recommend going through every line of code before using a userscript. As you can already imagine, a userscript can drastically alter a website's appearance. If you are not careful enough, you might click a button that you expect to do one thing but it does something completely different, so always err on the side of caution.</p>
<p>When writing userscripts, keep these things in mind:</p>
<ol>
<li>Always test your code in the dev console first and take care of all edge cases.</li>
<li>Prefer creating/manipulating DOM elements using built-in functions instead of modifying the innerHTML.</li>
<li>If you find any unexpected behaviour, try throwing a long delay and checking if that resolves the issue.</li>
</ol>
<p>You can read my other articles on my <a href="https://blog.renzhamin.com" target="_blank">blog</a>. Find me on <a href="https://twitter.com/renzhamin" target="_blank">Twitter</a> <a href="https://www.linkedin.com/in/renzhamin/" target="_blank">LinkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ A Practical Guide to Regular Expressions – Learn RegEx with Real Life Examples ]]>
                </title>
                <description>
                    <![CDATA[ What are Regular Expressions? Regular expressions, also known as regex, work by defining patterns that you can use to search for certain characters or words inside strings. Once you define the pattern you want to use, you can make edits, delete certa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/practical-regex-guide-with-real-life-examples/</link>
                <guid isPermaLink="false">66ba2e427787ec7052709309</guid>
                
                    <category>
                        <![CDATA[ Bash ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Regex ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Regular Expressions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tasnim Ferdous ]]>
                </dc:creator>
                <pubDate>Tue, 01 Aug 2023 20:42:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/The-Most-concise-guide-with-real-life-examples.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-what-are-regular-expressions">What are Regular Expressions?</h2>
<p>Regular expressions, also known as regex, work by defining patterns that you can use to search for certain characters or words inside strings.</p>
<p>Once you define the pattern you want to use, you can make edits, delete certain characters or words, substitute one thing for another, extract relevant information from a file or any string that contains that particular pattern, and so on.</p>
<h3 id="heading-why-should-you-learn-regex">Why Should You Learn Regex?</h3>
<p>Regex let you to do text processing in a way that can save you a lot of time. It can also introduce some fun in the process.</p>
<p>Using regex can make locating information much easier. Once you find your target, you can batch edit/replate/delete or whatever processing you need to do.</p>
<p>Some practical examples of using regex are batch file renaming, parsing logs, validating forms, making mass edits in a codebase, and recursive search.</p>
<p>In this tutorial, we're going to cover regex basics with the help of this <a href="https://regexr.com/" target="_blank">site</a>. Later on, I will introduce some regex challenges that you'll solve using Python. I'll also show you how to use tools like <code>sed</code> and <code>grep</code> with regex.</p>
<p>Like many things in life, regular expressions are one of those things that you can only truly understand by doing. I encourage you to play around with regex as you are going through this article.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>

<ul>
<li><a class="post-section-overview" href="#regex-basics">Regex Basics</a><ul>
<li><a class="post-section-overview" href="#exact-match">Exact match</a></li>
<li><a class="post-section-overview" href="#character-set">Character set</a><ul>
<li><a class="post-section-overview" href="#match-ranges-in-regex">Match ranges in regex</a></li>
<li><a class="post-section-overview" href="#match-any-character-not-in-the-set">Match any character not in the set</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#character-classes">Character classes</a></li>
<li><a class="post-section-overview" href="#heading-quantifiers">Quantifiers</a></li>
<li><a class="post-section-overview" href="#capture-groups">Capture groups</a><ul>
<li><a class="post-section-overview" href="#how-to-use-logical-or-in-regex">How to use logical OR in regex</a></li>
<li><a class="post-section-overview" href="#how-to-reference-capture-groups">How to reference capture groups</a></li>
<li><a class="post-section-overview" href="#how-to-name-capture-groups">How to name capture groups</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="post-section-overview" href="#how-to-use-regex-with-command-line-tools">How to use regex with command line tools</a><ul>
<li><a class="post-section-overview" href="#recursive-regex-search-with-grep">Recursive regex search with grep</a></li>
<li><a class="post-section-overview" href="#substitution-with-sed">Substitution with sed</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#lookarounds">Advanced Regex: Lookarounds</a><ul>
<li><a class="post-section-overview" href="#heading-lookbehinds">Lookbehinds</a></li>
<li><a class="post-section-overview" href="#heading-lookaheads">Lookaheads</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#practical-examples-of-regex">Practical Examples of Regex</a><ul>
<li><a class="post-section-overview" href="#logs-parsing">Logs parsing</a></li>
<li><a class="post-section-overview" href="#bulk-file-renaming">Bulk File Renaming</a></li>
<li><a class="post-section-overview" href="#email-validation">Email validation</a></li>
<li><a class="post-section-overview" href="#password-constraints">Password constraints</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#final-words">Final words</a></li>
</ul>

<p><a></a></p>
<h2 id="heading-regex-basics">Regex Basics</h2>
<p>A regular expression is nothing but a sequence of characters that match a pattern. Besides using literal characters (like 'abc'), there are some meta characters (*,+,? and so on) which have special purposes. There are also features like character classes which can help you simplify your regular expressions. </p>
<p>Before writing any regex, you'll need to learn about all the basic cases and edge cases for the pattern you are looking for. </p>
<p>For instance, if you want to match 'Hello World', do you want the line to start with 'Hello' or can it start with anything? Do you want exactly one space between 'Hello' and 'World' or there can be more? Can other characters come after 'World' or should the line end there? Do you care about case sensitivity? And so on.</p>
<p>These are the kind of questions you must have the answer to before you sit down to write your regex.</p>
<p><a></a></p>
<h3 id="heading-exact-match">Exact match</h3>
<p>The most basic form of regex involves matching a sequence of characters in a similar way as you can do with Ctrl-F in a text editor.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/exact_match.png" alt="exact_match" width="600" height="400" loading="lazy"></p>
<p>On the top you can see the number of matches, and on the bottom an explanation is provided for what the regex matches character by character.</p>
<p><a></a></p>
<h3 id="heading-character-set">Character set</h3>
<p>Regex character sets allow you to match any one character from a group of characters. The group is surrounded by square brackets []. </p>
<p>For example, <code>t[ah]i</code> matches "tai" and "thi". Here 't' and 'i' are fixed but between them can occur 'a' or 'h'.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_set.png" alt="match_set" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h4 id="heading-match-ranges-in-regex">Match ranges in regex</h4>
<p>Sometimes you may want to match a group of characters which are sequential in nature, such as any uppercase English letter. But writing all 26 letters would be quite tedious. </p>
<p>Regex solves this issue with ranges. The "-" acts as a range operator. Some valid ranges are shown below:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Range</td><td>Matches</td></tr>
</thead>
<tbody>
<tr>
<td>[A-Z]</td><td>uppercase letters</td></tr>
<tr>
<td>[a-z]</td><td>lowercase letters</td></tr>
<tr>
<td>[0-9]</td><td>Any digit</td></tr>
</tbody>
</table>
</div><p>You can also specify partial ranges, such as <code>[b-e]</code> to match any of the letters 'bcde' or <code>[3-6]</code> to match any of the numbers '3456'.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_set_ranges-1.png" alt="match_set_ranges" width="600" height="400" loading="lazy"></p>
<p>You are not limited to specifying only one range inside a character set. You can use multiple ranges and also combine them with any other additional character(s). Here, <code>[3-6u-w;]</code> will match any of '3456uvw' or semicolon ';'.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_set_ranges_multi-1.png" alt="match_set_ranges_multi" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h4 id="heading-match-any-character-not-in-the-set">Match any character not in the set</h4>
<p>If you prefix the set with a '^', the inverse operation will be performed. For example, <code>[^A-Z0-9]</code> will match anything except uppercase letters and digits.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_set_not.png" alt="match_set_not" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h3 id="heading-character-classes">Character classes</h3>
<p>While writing regex, you'll need to match certain groups such as digits quite often and multiple times in the same expression as well. </p>
<p>So for example, how would you match a pattern like 'letter-digit-letter-digit'? </p>
<p>With what you've learned up until now, you can come up with <code>[a-zA-Z]-[0-9]-[a-zA-z]-[0-9]</code>. This works, but you can see how the expression can get quite messy as the pattern length gets bigger.</p>
<p>To make the expression simpler, classes have been assigned to well-defined character groups such as digits. The following table shows these classes and their equivalent expression with character sets:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Class</td><td>Matches</td><td>Equivalent expression</td></tr>
</thead>
<tbody>
<tr>
<td>.</td><td>anything except newline</td><td>[^\n\r]</td></tr>
<tr>
<td>\w</td><td>word character</td><td>[a-zA-Z0-9_]</td></tr>
<tr>
<td>\W</td><td>non-word character</td><td>[^\w]</td></tr>
<tr>
<td>\d</td><td>digits</td><td>[0-9]</td></tr>
<tr>
<td>\D</td><td>non-digits</td><td>[^\d]</td></tr>
<tr>
<td>\s</td><td>space, tab, newlines</td><td>[ \t\r\n\f]</td></tr>
<tr>
<td>\S</td><td>non whitespace characters</td><td>[^\s]</td></tr>
</tbody>
</table>
</div><p>Character classes are quite handy and make your expressions much cleaner. We will use them extensively throughout this tutorial, so you can use this table as a reference point and come back here if you forget any of the classes.</p>
<p>Most of the time, we won't care about all the positions in a pattern. The "." class saves us from writing all possible characters in a set. </p>
<p>For example, <code>t..</code> matches anything that starts with t and any two characters afterwards. This may remind you of the SQL <code>LIKE</code> operator which would use <code>t%%</code> to accomplish the same thing.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_any.png" alt="match_any" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h3 id="heading-quantifiers">Quantifiers</h3>
<p>The word "pattern" and "repetition" go hand in hand. If you want to match a 3 digit number you can use <code>\d\d\d</code>. But what if you need to match 11 digits? You could write '\d' 11 times, but a general rule of thumb while writing regex or just doing any kind of programming is that if you find yourself repeating something more than twice, you are probably unaware of some feature. </p>
<p>In regex, you can use quantifiers for this purpose. To match 11 digits, you can simply write the expression <code>\d{11}</code>.</p>
<p>The table below lists the quantifiers you can use in regex:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Quantifier</td><td>Matches</td></tr>
</thead>
<tbody>
<tr>
<td>*</td><td>0 or more</td></tr>
<tr>
<td>?</td><td>0 or 1</td></tr>
<tr>
<td>+</td><td>1 or more</td></tr>
<tr>
<td>{n}</td><td>exactly n times</td></tr>
<tr>
<td>{n, }</td><td>n or more times</td></tr>
<tr>
<td>{n, m}</td><td>n to m times inclusive</td></tr>
</tbody>
</table>
</div><p>In this example, the expression <code>can\s+write</code> matches <code>can</code> followed by 1 or more whitespaces followed by <code>write</code>. But you can see 'canwrite' is not matched as <code>\s+</code> means at least one whitespace needs to be matched. This is useful when you are searching through text which is not trimmed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_multi_whitespaces.png" alt="match_multi_whitespaces" width="600" height="400" loading="lazy"></p>
<p>Can you guess what <code>can\s?write</code> will match?</p>
<p><a></a></p>
<h3 id="heading-capture-groups">Capture groups</h3>
<p>Capture groups are sub-expressions enclosed in parentheses (). You can have any number of capture groups, and even nested capture groups.</p>
<p>The expression <code>(The ){2}</code> matches 'The ' twice. But without a capture group, the expression <code>The {2}</code> would match 'The' followed by 2 spaces, as the quantifier will be applied on the space character and not on 'The ' as a group.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/capture_this.png" alt="capture_this" width="600" height="400" loading="lazy"></p>
<p>You can match any pattern inside capture groups as you would with any valid regex. Here <code>(is\s+){2}</code> matches if it finds 'is' followed by 1 or more spaces twice.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/capture_is.png" alt="capture_is" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h4 id="heading-how-to-use-logical-or-in-regex">How to use logical OR in regex</h4>
<p>You can use "|" to match multiple patterns. <code>This is (good|bad|sweet)</code> matches 'This is ' followed by any of 'good' or 'bad' or 'sweet'.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/or-1.png" alt="or" width="600" height="400" loading="lazy"></p>
<p>Again, you must understand the importance of capture groups here. Think about what the expression <code>This is good|bad|sweet</code> would match?</p>
<p> <img src="https://www.freecodecamp.org/news/content/images/2023/07/or_no_capture.png" alt="or_no_capture" width="600" height="400" loading="lazy"></p>
<p>With a capture group, <code>good|bad|sweet</code> is isolated from <code>This is</code>. But if it's not inside a capture group, the entire regex is only one group. So the expression <code>This is good|bad|sweet</code> will match if the string contains 'This is good' or 'bad' or 'sweet'.</p>
<p><a></a></p>
<h4 id="heading-how-to-reference-capture-groups">How to reference capture groups</h4>
<p>Capture groups can be referenced in the same expression or while performing replacements as you can see on the Replacement tab. </p>
<p>Most tools and languages allow you to reference the nth captured group with '\n'. In this site '$n' is used while referencing on replacement. The syntax for replacement will vary depending on the tools or language you're using. For JavaScript, for example, its '$n', while for Python its '\n'.</p>
<p>In the expression <code>(This) is \1 power</code>, 'This' is captured and then referenced with '\1', effectively matching <code>This is This power</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/refer_capture.png" alt="refer_capture" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h4 id="heading-how-to-name-capture-groups">How to name capture groups</h4>
<p>You can name your capture groups with the syntax <code>(?&lt;name&gt;pattern)</code> and backreference them in the same expression with <code>\k&lt;name&gt;</code>. </p>
<p>On replacement, referencing is done by <code>$&lt;name&gt;</code>. This is the syntax for JavaScript and can vary among languages. You can learn about the differences <a href="https://www.regular-expressions.info/named.html" target="_blank">here</a>. Also note that this feature might not be available in some languages.</p>
<p>In the expression <code>(?&lt;lang&gt;[\w+]+) is the best but \k&lt;lang&gt; .*</code>, the pattern <code>[\w+]+</code> is captured with the name 'lang' and backreferenced with <code>\k&lt;lang&gt;</code>. This pattern will match any word character or '+' character 1 or more times. The <code>.*</code> at the end of the regex matches any character 0 or more times. And finally on replacement, the referencing is done by <code>$&lt;lang&gt;</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/named_capture-1.png" alt="named_capture" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h2 id="heading-how-to-use-regex-with-command-line-tools">How to Use Regex with Command Line Tools</h2>
<p>There are good CLI tools available that let you perform regex from your terminal. These tools save you even more time as you can easily test different regex without writing code in some langauge and then compiling or interpreting it. </p>
<p>Some of the well-known tools are grep, sed, and awk. Let's look at a few examples to give you some ideas on how you can leverage these tools.</p>
<p><a></a></p>
<h3 id="heading-recursive-regex-search-with-grep">Recursive regex search with grep</h3>
<p>You can execute the power of regex through grep. Grep can search patterns in a file or perform recursive search.</p>
<p>If you are on Windows, you can install grep using winget. Run this command in powershell:</p>
<pre><code class="lang-powershell">winget install <span class="hljs-literal">-e</span> -<span class="hljs-literal">-id</span> GnuWin32.Grep
</code></pre>
<p>I will show you the solution to a challenge I created for a CTF competition at my university. </p>
<p>The file attached to the challenge is a <a href="https://github.com/renzhamin/regex-guide/blob/main/ripG.zip" target="_blank">zip file</a> that contains multiple levels of directories and a lot of files in it. The name of the competition was Coderush with flag format <code>coderush{flag is here}</code>. So you have to search for the pattern <code>coderush{.*}</code> which will match the flag format <code>coderush{any character here}</code>.</p>
<p>Unzip the file with <code>unzip ripG.zip</code> and cd into it with <code>cd ripG</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/huge_files.png" alt="huge_files" width="600" height="400" loading="lazy"></p>
<p>There are 358 directories and 8731 files. Instead of searching the pattern in the files one by one, you can employ grep like this:</p>
<pre><code class="lang-sh">grep --color -R <span class="hljs-string">"coderush{.*}"</span>
</code></pre>
<p>The "-R" flag enables recursive search.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/grep.png" alt="recursive search with grep" width="600" height="400" loading="lazy"></p>
<p>You can learn more about grep and its command line options <a href="https://www.freecodecamp.org/news/grep-command-in-linux-usage-options-and-syntax-examples/" target="_blank">here</a></p>
<p><a></a></p>
<h3 id="heading-substitution-with-sed">Substitution with sed</h3>
<p>You can use sed to perform insertion, deletion, substitution on text files by specifying a regex. If you are on windows, you can get sed from <a href="https://github.com/mbuilov/sed-windows" target="_blank">here</a>. Or if you use WSL, tools like grep and sed will already be available.</p>
<p>This is the most common usage of sed:</p>
<pre><code class="lang-sh">sed <span class="hljs-string">'s/pattern/replacement/g'</span> filename
<span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">${text}</span>"</span> | sed <span class="hljs-string">'s/pattern/replacement/g'</span>
</code></pre>
<p>Here, the option "g" is specified to replace all occurrences.</p>
<p>Some other useful options are <code>-n</code> to suppress the default behaviour of printing all lines and using p instead of g to print only the lines which are affected by the regex.</p>
<p>Let's take a look at the content of <a href="https://github.com/renzhamin/regex-guide/blob/main/texts.txt" target="_blank">texts.txt</a>.</p>
<pre><code>Hello rand chars World <span class="hljs-number">56</span> rand chars
Henlo <span class="hljs-number">52</span> rand chars W0rld rand chars
GREP rand chars Henlo <span class="hljs-number">62</span> rand chars
Henlo <span class="hljs-number">10</span> rand chars Henlo rand chars
GREP rand chars Henlo <span class="hljs-number">45</span> rand chars
</code></pre><p>Our task is replacing <code>Henlo number</code> with <code>Hello number</code> only in the lines where "GREP" is present. So, we are searching for the pattern <code>Henlo ([0-9]+)</code> which will match 'Henlo ' followed by 1 or more digits and all the digits are captured. Then our replacement string will be <code>Hello \1</code> – the '\1' is referencing the capture group containing the digits.</p>
<p>One way to accomplish that would be using grep to grep the lines which have "GREP" present then perform the replacement with sed.</p>
<pre><code class="lang-sh">grep <span class="hljs-string">"GREP"</span> texts.txt | sed -En <span class="hljs-string">'s/Henlo ([0-9]+)/Hello \1/p'</span>
</code></pre>
<p>The "-E" option enables extended regex without which you would need to escape the parentheses.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/grep_sed.png" alt="grep_sed" width="600" height="400" loading="lazy"></p>
<p>Or you could just use sed. Use <code>/pattern/</code> to restrict substitution on only the lines where pattern is present.</p>
<pre><code class="lang-sh">sed -En <span class="hljs-string">'/GREP/ s/Henlo ([0-9]+)/Hello \1/p'</span> texts.txt
</code></pre>
<p><a></a></p>
<h2 id="heading-advanced-regex-lookarounds">Advanced Regex: Lookarounds</h2>
<p>Lookaheads and Lookbehinds (together known as lookarounds) are features of regex that allow you to check the existence of a pattern without including it in the match.</p>
<p>You can think of them as zero width assertions – they assert the existence of a pattern but do not consume any characters in the match. These are very powerful features, but they're also computationally expensive. So make sure you keep an eye on performance if you are using them often.</p>
<p><a></a></p>
<h3 id="heading-lookbehinds">Lookbehinds</h3>
<p>Let's say you want to match the word 'linux', but you have 2 conditions.</p>
<ol>
<li>The word 'GNU' must occur before 'linux' occurs. If a line contains 'linux' but doesn't have 'GNU' before it, we want to discard that line.</li>
<li>We want to match only <code>linux</code> and nothing else.</li>
</ol>
<p>We already know how to satisfy the 1st condition. <code>GNU.*</code> will match 'GNU' followed by any number of characters. Then finally we match the word <code>linux</code>. This will match all of <code>GNU-any-characters-linux</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/GNU_LINUX.png" alt="GNU_LINUX" width="600" height="400" loading="lazy"></p>
<p>But how do we prevent matching <code>GNU.*</code> while still maintaining the 1st condition?</p>
<p>That's where a positive lookbehind comes in. You can mark a capture group as a positive lookbehind by prefixing it with <code>?&lt;=</code>. In this example, the expression becomes <code>(?&lt;=GNU.*)linux</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/positive_lookbehind.png" alt="positive_lookbehind" width="600" height="400" loading="lazy"></p>
<p>Now only <code>linux</code> is matched and nothing else.</p>
<p>Note that the expressions <code>(?&lt;=GNU.*)linux</code> and <code>linux(?&lt;=GNU.*)</code> will behave exactly the same. In the 2nd expression, although <code>linux</code> is before the lookbehind, there is <code>.*</code> after 'GNU' which matches <code>linux</code>. This means it satisfies the lookbehind. </p>
<p>To make it simpler, think about the pattern without the lookbehind. The pattern <code>GNU.*</code> will match 'GNU' and anything after it, in our case matching <code>linux</code>.</p>
<p>Now we can derive a generalized statement that the expression <code>(?&lt;=C)X</code> will match the pattern X – only if pattern C came before X (and C must not be included in the match).</p>
<p>You can also reverse the 1st condition. Match lines that contains the word <code>linux</code> only if <code>GNU</code> never came before it. This is called a negative lookbehind. The prefix in this case is <code>?&lt;!</code>. The inverse of the previous expression would be <code>(?&lt;!GNU.*)linux</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/negative_lookbehind.png" alt="negative_lookbehind" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h3 id="heading-lookaheads">Lookaheads</h3>
<p>Lookaheads are also assertions like lookbehinds, as you saw in the previous example. The only difference is that lookbehinds make an assertion before and lookaheads makes assertion after.</p>
<p>Let's say you have these two conditions:</p>
<ol>
<li>Match <code>Hello</code> only if <code>World</code> comes somewhere after it.</li>
<li>Match only Hello and nothing else.</li>
</ol>
<p>The prefix for a positive lookahead is <code>?=</code>. The expression <code>Hello(?=.*World)</code> will meet both conditions. This is similar to <code>Hello.*World</code> except that only <code>Hello</code> will be matched whereas <code>Hello.*World</code> will match 'Hello', 'World' and anything in between.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/postive_lookahead-1.png" alt="postive_lookahead" width="600" height="400" loading="lazy"></p>
<p>Similar to the example in a positive lookbehind, the expressions <code>Hello(?=.*World)</code> and <code>(?=.*World)Hello</code> are equivalent. Because the <code>.*</code> before 'World' matches <code>Hello</code>, satisfying the 1st condition.</p>
<p>A negative lookahead is just the complement of a negative lookbehind. You can use it by prefixing it with <code>?!</code>. <code>(?!World)Hello</code> will match <code>Hello</code> only if there is no <code>World</code> anywhere after it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/negative_lookahead.png" alt="negative_lookahead" width="600" height="400" loading="lazy"></p>
<p>Here is a summary of the syntax for lookarounds when you want to match the pattern X with assertion C.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Operation</td><td>RegEx</td></tr>
</thead>
<tbody>
<tr>
<td>positive lookahead</td><td><code>(?=C)X</code></td></tr>
<tr>
<td>negative lookahead</td><td><code>(?!C)X</code></td></tr>
<tr>
<td>positive lookbehind</td><td><code>(?&lt;=C)X</code></td></tr>
<tr>
<td>negative lookbehind</td><td><code>(?&lt;!C)X</code></td></tr>
</tbody>
</table>
</div><p><a></a></p>
<h2 id="heading-practical-examples-of-regex">Practical Examples of Regex</h2>
<p><a></a></p>
<h3 id="heading-logs-parsing">Logs parsing</h3>
<p>In this <a href="https://github.com/renzhamin/regex-guide/blob/main/log_train.txt" target="_blank">log file</a>, these are the lines which we care about:</p>
<pre><code>[<span class="hljs-number">1</span>/<span class="hljs-number">10000</span>] Train loss: <span class="hljs-number">11.30368</span>, Valid loss: <span class="hljs-number">8.95446</span>, <span class="hljs-attr">Elapsed_time</span>: <span class="hljs-number">7.58941</span>
[<span class="hljs-number">500</span>/<span class="hljs-number">10000</span>] Train loss: <span class="hljs-number">0.96180</span>, Valid loss: <span class="hljs-number">0.20098</span>, <span class="hljs-attr">Elapsed_time</span>: <span class="hljs-number">82.48651</span>
[<span class="hljs-number">1000</span>/<span class="hljs-number">10000</span>] Train loss: <span class="hljs-number">0.04051</span>, Valid loss: <span class="hljs-number">0.11927</span>, <span class="hljs-attr">Elapsed_time</span>: <span class="hljs-number">156.86243</span>
</code></pre><p>Our task is to extract the training loss and validation loss for purposes such as plotting loss over the epochs. We need to extract the training loss values like <code>11.30368, 0.96180, 0.04051</code> and put them in an array.</p>
<p>All the relevant values are prefixed with '<code>Train loss:</code>', so we can use this in our regex as it is. To match the float numbers we have to match some digits followed by a "<code>.</code>" and then followed by more digits. You can do this with <code>\d+\.\d+</code>. Because we want to keep track of these numbers, they should be inside a capture group.</p>
<p>As "." has special purpose in regex, when you want to match a "." character you have to escape it with a backslash. This is applicable for all characters with a special purpose. But you dont have to escape it inside a character set.</p>
<p>Putting it altogether, the expression for extracting training loss is <code>Train loss: (\d+\.\d+)</code>. We can use the same logic to extract validation loss with <code>Valid loss: (\d+\.\d+)</code>.</p>
<p>Here is one way to extract this information using Python:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> re

f = open(<span class="hljs-string">"log_train.txt"</span>, <span class="hljs-string">"r"</span>).read()

train_loss = re.findall(<span class="hljs-string">r'Train loss: (\d+\.\d+)'</span>, f)
valid_loss = re.findall(<span class="hljs-string">r'Valid loss: (\d+\.\d+)'</span>, f)

train_loss = [float(i) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> train_loss]
valid_loss = [float(i) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> valid_loss]

print(<span class="hljs-string">"train_loss ="</span>, train_loss)
print(<span class="hljs-string">""</span>)
print(<span class="hljs-string">"valid_loss ="</span>, valid_loss)
</code></pre>
<p>When there is one capture group, <code>re.findall</code> searches all the lines and returns the values inside the capture group in a list. </p>
<p>Any regex function only return strings, so the values are converted to floats and printed out. Then you can directly use them in another Python script as a list of floats.</p>
<p>This is the result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/extract_loss.png" alt="extract_loss" width="600" height="400" loading="lazy"></p>
<p>You could also use sed, save the output in train_losses.txt, and read from the file. First we use '/Train/' to target only the lines with 'Train' present then we are applying the same regex as before.</p>
<pre><code class="lang-sh">sed -En <span class="hljs-string">'/Train/ s/.*Train loss: ([0-9]+\.[0-9]+).*/\1/p'</span> log_train.txt | tee train_losses.txt
</code></pre>
<p>".*" is added at the start and end so that sed matches the contents of all the relevant lines. Then the entire line is replaced by the value of the capture group. The <code>tee</code> command is used to redirect the output of sed into train_losses.txt while also printing the contents in the terminal.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/extract_loss_sed.png" alt="extract_loss_sed" width="600" height="400" loading="lazy"></p>
<p>Take a moment to think about what would you need to extract the epochs. You have to extract 500 from [500/10000] for all such lines. The array should look like [1, 500, 1000, 1500, ...]. You can follow the same approach as we used for the previous example. </p>
<p>Note that if you want to match "<code>[</code>" or "<code>]</code>", you have to escape it. The answer is given <a target="_blank" href="https://github.com/renzhamin/regex-guide/blob/main/extract_epochs_array.py">here</a>.</p>
<p><a></a></p>
<h3 id="heading-bulk-file-renaming">Bulk File Renaming</h3>
<p>You have these <a href="https://github.com/renzhamin/regex-guide/tree/main/bulk-rename" target="_blank">files</a> with some random values as prefixes. You have to rename all files as 1.mp4, 2.mp4 and so on.
This is how the files were generated.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/create_files.png" alt="create_files" width="600" height="400" loading="lazy"></p>
<p>This is a common scenario where you have a list of files which have their sequence number in the name but there are also some other characters that you don't want.</p>
<p>The pattern has to match anything up to Episode then an underscore and then the number and .mp4 at the end. </p>
<p>The relevant value is the number before '.mp4' which we will put inside a capture group. <code>.*Episode_</code> will match everything up to the number. Then we can capture the number with <code>([0-9]+)</code> and also match .mp4 with <code>\.mp4</code>. </p>
<p>So the final regex is <code>.*Episode_([0-9]+)\.mp4</code>. As we want to keep the <code>.mp4</code> the replacement string will be <code>\1.mp4</code>.</p>
<p>This is one way to solve it using sed.</p>
<pre><code class="lang-sh"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> *.mp4; <span class="hljs-keyword">do</span>
    newname=$(<span class="hljs-built_in">echo</span> <span class="hljs-variable">$i</span> | sed -En <span class="hljs-string">'s/.*Episode_([0-9]+)\.mp4/\1.mp4/p'</span>)
    mv <span class="hljs-variable">$i</span> <span class="hljs-variable">$newname</span>
<span class="hljs-keyword">done</span>;ls
</code></pre>
<p>First the new name is saved in a variable and then the mv command is used to rename the file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/bulk_rename.png" alt="bulk_rename" width="600" height="400" loading="lazy"></p>
<p>Could we have just used <code>.*</code> in place of <code>.*Episode_</code> ? In this example, yes. But there might be filenames such as <code>Steins_Gate0.mp4</code> where the <code>0</code> is part of the movie name and you didn't really want to rename this file so its always better to be as specific as possible.</p>
<p>What if some files were named as "Random_Episode6.mp4"? The difference being, there is no underscore after Episode. What change will you need to make?</p>
<p>The answer is that you'll need to add a "?" after the "_" to make it optional. The regex will be <code>.*Episode_?([0-9]+)\.mp4</code>.</p>
<p><a></a></p>
<h3 id="heading-email-validation">Email validation</h3>
<p>There are all sorts of complicated regex for validating email.</p>
<p>Here is a simple one: <code>^[^@ ]+@[^@.]+\.\w+$</code>. It matches the format <code>A@B.C</code></p>
<p>The table below breaks down this pattern into smaller pieces:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Pattern</td><td>Matches</td></tr>
</thead>
<tbody>
<tr>
<td><code>^</code></td><td>start of line</td></tr>
<tr>
<td><code>[^@ ]+</code></td><td>anything except "@" and space character</td></tr>
<tr>
<td><code>@[^@.]+</code></td><td>@ followed by anything except "@" and "." characters</td></tr>
<tr>
<td><code>\.\w+</code></td><td>"." followed by word characters</td></tr>
<tr>
<td><code>$</code></td><td>end of line</td></tr>
</tbody>
</table>
</div><p><img src="https://www.freecodecamp.org/news/content/images/2023/07/email_validation.png" alt="email_validation" width="600" height="400" loading="lazy"></p>
<p>In the regexr site, you can enable the multline flag from the Flags tab in the upper right corner. The 'gm' at the end indicates that the multiline flag is enabled.</p>
<p>We can see that line 2,3,5,6 didn't match. Can you find out the reason and which part of the regex is responsible for disqualifying it?</p>
<p>The answer is given <a href="https://github.com/renzhamin/regex-guide/blob/main/email_validation.md" target="_blank">here</a></p>
<p><a></a></p>
<h3 id="heading-password-constraints">Password constraints</h3>
<p>You can also use regex to impose constraints. Here we will uncover the power of positive lookaheads. </p>
<p>Lets say we want to accept a string only if there is a digit in it. You already know how to find a digit with the '\d' class. To accomplish that, we can use <code>[^\d]*\d</code>. This will match any non-digit character 0 or more times and then match a digit. </p>
<p>We can also use the expression <code>.*\d</code> to match one digit. So if there is no digit in the string then the lookahead will fail and the none of the characters of that string will be matched, returning an empty string "". </p>
<p>When we are using a programming language, we can check if the regex returned an empty string and determine that the constraints are not satisfied.</p>
<p>We will create a regex which imposes the following criteria:</p>
<ol>
<li>Minimum 8 characters and maximum 16 characters.</li>
<li>At least one lower case letter.</li>
<li>At least one upper case letter.</li>
<li>At least one number.</li>
</ol>
<p>To achieve this, you can use positive lookaheads. This is the regex:</p>
<p><code>^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,16}$</code></p>
<p>The table below explains which part of the regex imposes which constraint:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Pattern</td><td>Imposed Constraint</td></tr>
</thead>
<tbody>
<tr>
<td><code>.{8,16}</code></td><td>min 8 and max 16 characters</td></tr>
<tr>
<td><code>(?=.*[a-z])</code></td><td>minimum one lower case letter</td></tr>
<tr>
<td><code>(?=.*[A-Z])</code></td><td>minimum one upper case letter</td></tr>
<tr>
<td><code>(?=.*\d)</code></td><td>minimum one digit</td></tr>
</tbody>
</table>
</div><p><img src="https://www.freecodecamp.org/news/content/images/2023/07/pass_constraints.png" alt="pass_constraints" width="600" height="400" loading="lazy"></p>
<p>What modification you would need for imposing at least 5 upper case letters?</p>
<p>You may think <code>(?=.*[A-Z]{5,})</code> will do the job. But this expression requires all the 5 letters to be together. A string like <code>rand-ABCDE-rand</code> will be matched but <code>0AxBCDxE0</code> will not be matched even though it has 5 upper case letters (as they are not adjacent).</p>
<p>Yet again, we have capture groups coming to the rescue. We want to match 5 uppercase letters anywhere in the string. We already know that we can match 1 uppercase letter with <code>.*[A-Z]</code>. Now we will put them inside a capture group and attach a quantifier of minimum 5. The expression will be <code>(.*[A-Z]){5,}</code>.</p>
<p>Here is the final answer:</p>
<p>In place of <code>(?=.*[A-Z])</code> you will need <code>(?=(.*[A-Z]){5,})</code>. The expression becomes <code>^(?=.*[a-z])(?=(.*[A-Z]){5,})(?=.*\d).{8,16}$</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/pass_5_upper.png" alt="pass_5_upper" width="600" height="400" loading="lazy"></p>
<p>You could also require that the password not contain certain words to enforce stronger passwords. </p>
<p>For example, we want to reject the password if contains <code>pass</code> or <code>1234</code>. Negative lookaheads is the tool for this job. The regex would be <code>^(?!.*(pass|1234)).*$</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/restrict_words-1.png" alt="restrict_words-1" width="600" height="400" loading="lazy"></p>
<p>In this regex, we put <code>pass</code> and <code>1234</code> inside a capture group and used the logical OR operator. This capture group is nested inside another capture group which is prefixed with <code>?!.*</code>. This makes it a negative lookahead that matches if there are at least 8 characters by <code>.{8,}</code> with the condition that, <code>pass</code> or <code>1234</code> can't be present anywhere in the string.</p>
<p><a></a></p>
<h2 id="heading-final-words">Final Words</h2>
<p>I hope you got a good amount of practice while going through this article. It's ok if you forget some syntax. What's important is understanding the core concepts and having a good idea of what's possible with regex. Then, if you forget a pattern, you can just google it or reference a cheatsheet. </p>
<p>The more you practice, the more you will get by without outside help. Eventually you will be able write super complex and effective regex completely offline. </p>
<p>There are already some good regex cheatsheets out there, so I wanted to create something more in-depth here that you can reference for the core concepts and common use cases. </p>
<p>If you're looking for a cheatsheet, the one from <a href="https://quickref.me/regex.html" target="_blank">QuickRef</a> is helpful. It's a good place to recall the syntax and they also provide some basic overview of regex related functions in various programming languages.</p>
<p>Most regex techniques are the same in all programming languages and tools – but certain tools might offer additional features. So do some research on the tool you are using to pick the best one for you.</p>
<p>My final suggestion would be not to force using regex just because you can. A lot of the times a regular <code>string.find()</code> is enough to get the job done. But if you live in the terminal, you really can do a lot just with regex for sure.</p>
<p>If you like this type of article, you may keep an eye on my <a href="https://blog.renzhamin.com" target="_blank">blog</a> or twitter.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
