<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zero | One</title>
    <description>The latest articles on DEV Community by Zero | One (@shunya_ek).</description>
    <link>https://dev.to/shunya_ek</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F233717%2Fce6b2b18-565f-4a72-b612-2b3ae18d572c.jpeg</url>
      <title>DEV Community: Zero | One</title>
      <link>https://dev.to/shunya_ek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shunya_ek"/>
    <language>en</language>
    <item>
      <title>Introducing SheetDB: Turn Google Sheets into your database</title>
      <dc:creator>Zero | One</dc:creator>
      <pubDate>Fri, 05 Dec 2025 21:22:28 +0000</pubDate>
      <link>https://dev.to/shunya_ek/introducing-sheetdb-2jme</link>
      <guid>https://dev.to/shunya_ek/introducing-sheetdb-2jme</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This year, I participated in &lt;a href="https://kiroween.devpost.com/" rel="noopener noreferrer"&gt;Kiroween&lt;/a&gt;, and for this hackathon, I built &lt;strong&gt;SheetDB&lt;/strong&gt;—a project that transforms Google Sheets into a fully operational relational database with SQL capabilities, multiple tables, relationships, and performance that's 500-1000x faster than making direct API calls to Google Sheets to achieve a similar goal.&lt;/p&gt;

&lt;p&gt;You might wonder: why use Google Sheets as a database when there are fully-featured database solutions like PostgreSQL, MySQL, or managed services like Supabase, Firebase, and Appwrite? The answer is simple: &lt;strong&gt;because it is fun to hack something like this and a lot useful at the same time and more importantly, because I can&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Benefits of Using Google Sheets as a Database
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero Hosting Costs&lt;/strong&gt; - Leverage Google's infrastructure for free, with near-unlimited bandwidth and reliability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in Admin Interface&lt;/strong&gt; - Google Sheets provides a familiar, powerful UI that everyone already knows how to use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Setup&lt;/strong&gt; - No database installation, no server configuration, no DevOps—just publish your sheet and start querying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time Collaboration&lt;/strong&gt; - Multiple users can edit data simultaneously with Google's built-in collaboration features&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version History&lt;/strong&gt; - Every change is tracked automatically, providing a built-in audit trail&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unlimited Writes via Forms&lt;/strong&gt; - Use Google Forms for data collection without consuming API quota&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The SheetDB Ecosystem
&lt;/h3&gt;

&lt;p&gt;SheetDB is now a comprehensive ecosystem with three main components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/whitebumblebee/sheetdb" rel="noopener noreferrer"&gt;SheetDB Core&lt;/a&gt;&lt;/strong&gt; - Python library and CLI tool providing the core database functionality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/whitebumblebee/sheetdb-js" rel="noopener noreferrer"&gt;sheetdb-js&lt;/a&gt;&lt;/strong&gt; - JavaScript/TypeScript client with React hooks for frontend integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/whitebumblebee/sheetdb-server" rel="noopener noreferrer"&gt;sheetdb-server&lt;/a&gt;&lt;/strong&gt; - FastAPI REST API server that exposes SheetDB to any language or client&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Inspiration
&lt;/h2&gt;

&lt;p&gt;There have been several attempts at using Google Sheets as a database for simple MVPs and CRUD applications. The most notable is &lt;strong&gt;&lt;a href="https://github.com/theapache64/retrosheet" rel="noopener noreferrer"&gt;Retrosheet&lt;/a&gt;&lt;/strong&gt;, which elegantly turns Google Sheets into a single-table database and which was the main inspiration to this project(all credits to &lt;a href="https://x.com/theapache64" rel="noopener noreferrer"&gt;theapache64&lt;/a&gt;). It's brilliant for quick prototypes, but as I had more ambitious project in my mind on top of this which addresses following issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No real database queries&lt;/strong&gt; - Couldn't write SQL or perform complex filtering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single table only&lt;/strong&gt; - All data had to live in one sheet with no relationships&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No relational data&lt;/strong&gt; - Impossible to model users → posts, products → orders, or any other relationships&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slow performance&lt;/strong&gt; - Every query hits the Google Sheets API directly (~500ms per query)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limits&lt;/strong&gt; - Limited to 100 requests per 100 seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited flexibility&lt;/strong&gt; - Rigid architecture with no room for customisation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I needed something more powerful, more flexible, but still free and easy to deploy.&lt;/p&gt;

&lt;p&gt;That's when I decided to build &lt;strong&gt;SheetDB&lt;/strong&gt; to fill exactly those gaps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What SheetDB Does: A Complete Solution
&lt;/h2&gt;

&lt;p&gt;SheetDB transforms Google Sheets into a &lt;strong&gt;full-featured relational database&lt;/strong&gt; with all the power of traditional databases, but zero hosting costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;h4&gt;
  
  
  🗄️ True Relational Database
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple tables with relationships&lt;/strong&gt; - Define foreign keys and join data across sheets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full SQL support&lt;/strong&gt; - JOINs, aggregations, subqueries, WHERE clauses, GROUP BY, ORDER BY&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema validation&lt;/strong&gt; - Type checking and constraint enforcement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary keys and referential integrity&lt;/strong&gt; - Maintain data consistency
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Complex joins across multiple tables
&lt;/span&gt;&lt;span class="n"&gt;hunts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    SELECT h.hunt_date, m.name as monster, hu.name as hunter, h.success
    FROM hunts h
    JOIN monsters m ON h.monster_id = m.id
    JOIN hunters hu ON h.hunter_id = hu.id
    WHERE h.success = true
    ORDER BY h.hunt_date DESC
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ⚡ 500x Performance Boost
&lt;/h4&gt;

&lt;p&gt;The secret sauce: &lt;strong&gt;DuckDB caching layer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sync data once from Google Sheets into in-memory DuckDB&lt;/li&gt;
&lt;li&gt;All queries run against the cache (~1ms vs ~500ms)&lt;/li&gt;
&lt;li&gt;Smart sync strategies: aggressive, lazy, scheduled, or manual&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Performance Comparison:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Direct API (Retrosheet)&lt;/th&gt;
&lt;th&gt;SheetDB (Cached)&lt;/th&gt;
&lt;th&gt;Speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Single query&lt;/td&gt;
&lt;td&gt;500ms&lt;/td&gt;
&lt;td&gt;1ms&lt;/td&gt;
&lt;td&gt;500x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100 queries&lt;/td&gt;
&lt;td&gt;50 seconds&lt;/td&gt;
&lt;td&gt;0.1 seconds&lt;/td&gt;
&lt;td&gt;500x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Join query&lt;/td&gt;
&lt;td&gt;Not possible&lt;/td&gt;
&lt;td&gt;1ms&lt;/td&gt;
&lt;td&gt;∞&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# One sync, unlimited fast queries
&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# ~2 seconds (one-time cost)
&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM monsters&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# ~1ms each!
# Total: ~2 seconds vs Retrosheet's ~500 seconds
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🎯 Three Flexible Operating Modes
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;1. Public Mode&lt;/strong&gt; (No Authentication)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read from published Google Sheets without any credentials&lt;/li&gt;
&lt;li&gt;Unlimited reads from public CSV exports&lt;/li&gt;
&lt;li&gt;Optional unlimited writes via Google Forms&lt;/li&gt;
&lt;li&gt;Perfect for prototypes and public data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Authenticated Mode&lt;/strong&gt; (Full CRUD)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full create, read, update, delete operations&lt;/li&gt;
&lt;li&gt;Private data support with service account authentication&lt;/li&gt;
&lt;li&gt;Complete control over all operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Hybrid Mode&lt;/strong&gt; (Best of Both) ⚡ &lt;strong&gt;Recommended&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public reads (unlimited, fast)&lt;/li&gt;
&lt;li&gt;Authenticated writes (controlled)&lt;/li&gt;
&lt;li&gt;Optimal performance for production apps
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Public mode - zero setup!
&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SheetDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_public_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.google.com/spreadsheets/d/YOUR_ID/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Authenticated mode - full control
&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SheetDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;creds.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spreadsheet_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Hybrid mode - best of both worlds
&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SheetDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sheet_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;creds.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hybrid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  📝 Multiple Read/Write Options
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;For Reads:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Direct SQL queries with DuckDB&lt;/li&gt;
&lt;li&gt;Django-like ORM for Python&lt;/li&gt;
&lt;li&gt;REST API for any frontend&lt;/li&gt;
&lt;li&gt;JavaScript client with React hooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Writes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Direct API writes (authenticated mode)&lt;/li&gt;
&lt;li&gt;Google Forms integration (unlimited writes, no API quota!)&lt;/li&gt;
&lt;li&gt;Automated form creation via Forms API
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Unlimited writes via Google Forms (no API quota!)
&lt;/span&gt;&lt;span class="n"&gt;form_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enable_forms_writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;monsters&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auto_create&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# Write 1000 records - no limits!
&lt;/span&gt;    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_via_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;monsters&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Monster_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;danger_level&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🤖 Everything Automated
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto-generate schemas&lt;/strong&gt; from existing sheets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-create sheets&lt;/strong&gt; from schema definitions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-create forms&lt;/strong&gt; for data entry&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-sync data&lt;/strong&gt; on schedule&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-validate&lt;/strong&gt; against schema
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate schema from existing sheets&lt;/span&gt;
sheetdb schema generate &lt;span class="nt"&gt;--credentials&lt;/span&gt; creds.json &lt;span class="nt"&gt;--spreadsheet-id&lt;/span&gt; YOUR_ID

&lt;span class="c"&gt;# Create sheets from schema&lt;/span&gt;
sheetdb sheets create &lt;span class="nt"&gt;--schema&lt;/span&gt; schema.yaml

&lt;span class="c"&gt;# Create forms for all tables&lt;/span&gt;
sheetdb forms init &lt;span class="nt"&gt;--schema&lt;/span&gt; schema.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🌐 Framework Agnostic
&lt;/h4&gt;

&lt;p&gt;Works with any Python or JavaScript framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Django (with ORM adapter)&lt;/li&gt;
&lt;li&gt;✅ FastAPI (REST API server)&lt;/li&gt;
&lt;li&gt;✅ Flask&lt;/li&gt;
&lt;li&gt;✅ React (with hooks)&lt;/li&gt;
&lt;li&gt;✅ Vue&lt;/li&gt;
&lt;li&gt;✅ Angular&lt;/li&gt;
&lt;li&gt;✅ Next.js&lt;/li&gt;
&lt;li&gt;✅ Vanilla JS/Python&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Built It: Architecture and Technical Decisions
&lt;/h2&gt;

&lt;p&gt;Building SheetDB required careful architectural decisions to balance simplicity, performance, and flexibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────┐
│  Application Layer (React, Django, FastAPI, etc.)  │
└─────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────┐
│  Interface Layer (JS Client, REST API, ORM)         │
└─────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────┐
│  Core SheetDB Library (Unified API)                 │
│  ├─ Sheets Gateway (API + Public URLs)              │
│  ├─ DuckDB Engine (Relational Cache)                │
│  └─ Schema Config (Validation)                      │
└─────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────┐
│  Data Sources (Google Sheets, Public URLs, Forms)   │
└─────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Technical Decisions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. DuckDB as Cache Layer
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; Google Sheets API is slow (~500ms per query) and rate-limited (100 requests per 100 seconds).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Cache data in DuckDB, an embedded analytical database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why DuckDB?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In-memory&lt;/strong&gt; - No disk I/O, blazing fast queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Columnar storage&lt;/strong&gt; - Optimized for analytical queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vectorized execution&lt;/strong&gt; - SIMD operations for speed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full SQL support&lt;/strong&gt; - JOINs, aggregations, subqueries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedded&lt;/strong&gt; - No separate server process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DuckDBEngine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;:memory:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Create table from sheet data
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE OR REPLACE TABLE &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; AS SELECT * FROM data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Execute with parameter binding (SQL injection safe)
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; 500-1000x performance improvement!&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Three Operating Modes
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; Different use cases need different authentication strategies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Support three modes with seamless switching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Public Mode Implementation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PublicSheetsReader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_sheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sheet_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Construct CSV export URL
&lt;/span&gt;        &lt;span class="n"&gt;csv_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.google.com/spreadsheets/d/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/export?format=csv&amp;amp;gid=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="c1"&gt;# Fetch and parse CSV (no authentication!)
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; Zero setup for prototypes, full control for production.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Forms API for Unlimited Writes
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; Google Sheets API has strict rate limits for writes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Use Google Forms API to create forms that write to sheets without consuming API quota.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FormsAPIGateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spreadsheet_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sheet_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Create form via Forms API
&lt;/span&gt;        &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forms_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forms&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;info&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}}).&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# Link to spreadsheet
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;link_to_spreadsheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;formId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;spreadsheet_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sheet_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;formId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;submit_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry_mapping&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Submit via HTTP POST (no API quota!)
&lt;/span&gt;        &lt;span class="n"&gt;form_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;entry_mapping&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;submit_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; Unlimited writes for high-volume data collection!&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Schema-First Design
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; Google Sheets are untyped and unstructured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; YAML schema configuration with validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;tables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;monsters&lt;/span&gt;
    &lt;span class="na"&gt;sheet_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Monsters&lt;/span&gt;
    &lt;span class="na"&gt;primary_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
    &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;name&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;danger_level&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;

&lt;span class="na"&gt;relationships&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hunt_monster&lt;/span&gt;
    &lt;span class="na"&gt;from_table&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hunts&lt;/span&gt;
    &lt;span class="na"&gt;from_column&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;monster_id&lt;/span&gt;
    &lt;span class="na"&gt;to_table&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;monsters&lt;/span&gt;
    &lt;span class="na"&gt;to_column&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type safety and validation&lt;/li&gt;
&lt;li&gt;Automatic code generation&lt;/li&gt;
&lt;li&gt;Clear documentation&lt;/li&gt;
&lt;li&gt;Enables relational queries&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5. Framework Agnostic Core
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; Different projects use different frameworks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Build a clean core library with adapters for specific frameworks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core Library&lt;/strong&gt; - Framework-independent SheetDB class&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Django Adapter&lt;/strong&gt; - ORM layer mimicking Django models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REST API&lt;/strong&gt; - FastAPI server for universal access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JS Client&lt;/strong&gt; - npm package for frontend frameworks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Works with any framework, no vendor lock-in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges I Faced and How I Solved Them
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Challenge 1: Google API Rate Limits
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The Sheets API has strict rate limits (100 requests per 100 seconds).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; DuckDB caching. Sync once, query unlimited times. This turned a limitation into our biggest performance advantage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impact:&lt;/strong&gt; 500x performance improvement and no rate limit issues for reads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 2: Relational Data in Flat Sheets
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Google Sheets are inherently flat, but we needed relationships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Built a schema system that maps sheets to tables and defines foreign keys, then DuckDB handles the joins in-memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Schema defines relationships
&lt;/span&gt;&lt;span class="n"&gt;relationships&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;from_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hunts&lt;/span&gt;
    &lt;span class="n"&gt;from_column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;monster_id&lt;/span&gt;
    &lt;span class="n"&gt;to_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;monsters&lt;/span&gt;
    &lt;span class="n"&gt;to_column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;

&lt;span class="c1"&gt;# DuckDB executes joins
&lt;/span&gt;&lt;span class="n"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;monster_name&lt;/span&gt;
&lt;span class="n"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;hunts&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;
&lt;span class="n"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;monsters&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monster_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Challenge 3: Type Safety Without a Real Database
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Sheets store everything as strings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Implemented automatic type inference and validation, converting strings to proper types (integers, dates, booleans) based on the schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;infer_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Challenge 4: Multiple Framework Support
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Supporting Django, FastAPI, React, and vanilla JS meant building for many different environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Used the adapter pattern to keep framework-specific code separate from the core library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Clean architecture with well-defined interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 5: Authentication Complexity
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Google's authentication can be tricky and intimidating for beginners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Simplified it with three modes: public (no auth), authenticated (service account), and hybrid. Each mode has clear setup instructions and examples.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation:&lt;/strong&gt; Created comprehensive guides with step-by-step instructions for each mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 6: Schema Management
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Manually writing schemas is tedious and error-prone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Built three ways to create schemas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Interactive CLI&lt;/strong&gt; - Step-by-step guided creation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-generation&lt;/strong&gt; - Infer from existing sheets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual YAML&lt;/strong&gt; - Full control for advanced users
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Interactive creation&lt;/span&gt;
sheetdb schema create

&lt;span class="c"&gt;# Auto-generation&lt;/span&gt;
sheetdb schema generate &lt;span class="nt"&gt;--credentials&lt;/span&gt; creds.json &lt;span class="nt"&gt;--spreadsheet-id&lt;/span&gt; YOUR_ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Challenge 7: Forms API Integration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The Forms API is powerful but complex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Abstracted it into simple functions: create a form, link it to a sheet, and get unlimited writes. All automated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# One line to enable unlimited writes
&lt;/span&gt;&lt;span class="n"&gt;form_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enable_forms_writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;monsters&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auto_create&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Accomplishments I'm Proud Of
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🎯 True Relational Database
&lt;/h3&gt;

&lt;p&gt;We didn't just add features—we fundamentally changed what's possible. Multiple tables, foreign keys, SQL joins, aggregations. It's a real database now.&lt;/p&gt;

&lt;h3&gt;
  
  
  🆓 Still Completely Free
&lt;/h3&gt;

&lt;p&gt;Despite all the advanced features, SheetDB remains free to use. No hosting costs, no bandwidth limits, no hidden fees. Google's infrastructure handles everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  🤖 Automation That Actually Works
&lt;/h3&gt;

&lt;p&gt;From schema generation to form creation, everything can be automated. This saves hours of manual setup and reduces errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  📚 Comprehensive Documentation
&lt;/h3&gt;

&lt;p&gt;I built extensive documentation with examples, tutorials, troubleshooting guides, and API references. The Django app and React examples show real-world usage.(check repo link)&lt;/p&gt;

&lt;h3&gt;
  
  
  🎨 Beautiful Demo Apps
&lt;/h3&gt;

&lt;p&gt;The Monster Hunt Tracker (Django) showcases the full stack with a Kiro dark mode + Halloween theme. It's functional and fun. (check repo link)&lt;/p&gt;

&lt;h3&gt;
  
  
  🌐 Framework Flexibility
&lt;/h3&gt;

&lt;p&gt;Works with Python, JavaScript, Django, FastAPI, React—whatever you're building with. Not locked into any specific framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 Production-Ready
&lt;/h3&gt;

&lt;p&gt;Type safety, validation, error handling, logging, testing. SheetDB isn't just a prototype—it's ready for real applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Technical Lessons
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Caching is Everything&lt;/strong&gt;&lt;br&gt;
The DuckDB layer transformed performance. Sometimes the best solution isn't faster APIs, it's not calling them at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Constraints Breed Creativity&lt;/strong&gt;&lt;br&gt;
Google Sheets' limitations forced us to think differently. The result is more innovative than a traditional database wrapper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Simplicity Wins&lt;/strong&gt;&lt;br&gt;
The three operating modes (public, authenticated, hybrid) cover 99% of use cases. We resisted adding complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Documentation Matters&lt;/strong&gt;&lt;br&gt;
Good docs with examples are as important as good code. We invested heavily in guides, tutorials, and working demos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Framework Agnostic is Hard&lt;/strong&gt;&lt;br&gt;
Supporting multiple frameworks requires careful architecture. The adapter pattern and clean interfaces were essential.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Kiro: Spec-Driven Development Meets Vibe Coding
&lt;/h2&gt;

&lt;p&gt;Building SheetDB with Kiro combined two approaches: &lt;strong&gt;spec-driven development&lt;/strong&gt; for core architecture and &lt;strong&gt;vibe coding&lt;/strong&gt; for rapid iteration—achieving ~20x faster development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spec-Driven Development
&lt;/h3&gt;

&lt;p&gt;For critical features, I used Kiro's structured workflow with formal requirements (&lt;code&gt;.kiro/specs/sheetdb/requirements.md&lt;/code&gt;), detailed design documents with correctness properties, and phased task lists. This provided clear direction, testable acceptance criteria, and maintainable architecture for the database engine, ORM layer, and API contracts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vibe Coding
&lt;/h3&gt;

&lt;p&gt;For UI/UX and documentation, I used conversational iteration with Kiro:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Theme Design:&lt;/strong&gt; "Make it like Kiro dark mode with Halloween emojis" → Complete CSS in 20 minutes (vs 4-6 hours)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; "Cover authentication for 3 Google APIs" → Production-ready guides in 30 minutes (vs 2-3 days)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging:&lt;/strong&gt; Screenshot of error → Complete fix with automation scripts in 10 minutes (vs 1-2 hours)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Key Insight
&lt;/h3&gt;

&lt;p&gt;Use &lt;strong&gt;spec-driven&lt;/strong&gt; when correctness matters most (architecture, core logic). Use &lt;strong&gt;vibe coding&lt;/strong&gt; when speed matters most (styling, docs, automation). Together: production-ready system in ~2 hours vs ~4-5 days traditional development.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next for SheetDB
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Short-term (Next 3 Months)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Incremental Sync and some improvements&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only update changed data instead of full reloads&lt;/li&gt;
&lt;li&gt;Track modifications and sync deltas&lt;/li&gt;
&lt;li&gt;Free analytics for data via Google Forms&lt;/li&gt;
&lt;li&gt;Finish and police the product, write detailed documentation and host it for users.&lt;/li&gt;
&lt;li&gt;expose sheetdb server to be used for any other project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. More Examples&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flask app example&lt;/li&gt;
&lt;li&gt;Full React App example using sheetdb-js&lt;/li&gt;
&lt;li&gt;Show SheetDB working with every major framework&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Medium-term (6 Months)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Multi-Spreadsheet Support&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Combine data from multiple Google Sheets&lt;/li&gt;
&lt;li&gt;Join across spreadsheets&lt;/li&gt;
&lt;li&gt;Aggregate data from multiple sources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Advanced and more complete SQL Relationships&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many-to-many relationships&lt;/li&gt;
&lt;li&gt;Polymorphic associations&lt;/li&gt;
&lt;li&gt;Self-referential relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Schema Migrations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version control for schemas&lt;/li&gt;
&lt;li&gt;Add columns, change types&lt;/li&gt;
&lt;li&gt;Migrate data safely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Offline Support&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local-first architecture with sync&lt;/li&gt;
&lt;li&gt;Work offline, sync when connected&lt;/li&gt;
&lt;li&gt;Conflict resolution&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Long-term (1 Year+)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Visual Schema Builder&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web UI for designing schemas&lt;/li&gt;
&lt;li&gt;No YAML required&lt;/li&gt;
&lt;li&gt;Drag-and-drop table relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. GraphQL API&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alternative to REST API&lt;/li&gt;
&lt;li&gt;More flexible queries from frontends&lt;/li&gt;
&lt;li&gt;Better for complex data fetching&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try SheetDB Today!
&lt;/h2&gt;

&lt;p&gt;Ready to build your next project with zero hosting costs and database power?&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Start (5 Minutes)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;git+https://github.com/yourusername/sheetdb.git

&lt;span class="c"&gt;# Initialize from public URL (no auth required!)&lt;/span&gt;
sheetdb init &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s2"&gt;"https://docs.google.com/spreadsheets/d/YOUR_ID/"&lt;/span&gt;

&lt;span class="c"&gt;# Start querying!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sheetdb&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SheetDB&lt;/span&gt;

&lt;span class="c1"&gt;# Connect to your sheet
&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SheetDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_public_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.google.com/spreadsheets/d/YOUR_ID/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Query with SQL
&lt;/span&gt;&lt;span class="n"&gt;monsters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM monsters WHERE danger_level &amp;gt; 7&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# That's it! 🎉
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/whitebumblebee/sheetdb" rel="noopener noreferrer"&gt;github.com/whitebumblebee/sheetdb&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; &lt;a href="https://github.com/whitebumblebee/sheetdb/tree/main/documentation" rel="noopener noreferrer"&gt;Complete guides and tutorials&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Examples:&lt;/strong&gt; &lt;a href="https://github.com/whitebumblebee/sheetdb/tree/main/examples" rel="noopener noreferrer"&gt;Working code examples&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo App:&lt;/strong&gt; &lt;a href="https://github.com/whitebumblebee/sheetdb/tree/main/django_app" rel="noopener noreferrer"&gt;Monster Hunt Tracker&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Join the Community
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ Star the project on GitHub&lt;/li&gt;
&lt;li&gt;🐛 Report bugs and request features&lt;/li&gt;
&lt;li&gt;💡 Share your projects built with SheetDB&lt;/li&gt;
&lt;li&gt;📝 Contribute to documentation&lt;/li&gt;
&lt;li&gt;🤝 Help other developers&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Built with 🎃 Kiroween spirit&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Because the best solutions come from working around limitations, not throwing money at them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Regular Expressions(RegEx) in Python</title>
      <dc:creator>Zero | One</dc:creator>
      <pubDate>Thu, 17 Oct 2019 22:36:35 +0000</pubDate>
      <link>https://dev.to/shunya_ek/regular-expressions-regex-in-python-2non</link>
      <guid>https://dev.to/shunya_ek/regular-expressions-regex-in-python-2non</guid>
      <description>&lt;p&gt;Any developer or a wanna-be developer knows how useful regular expressions can be while dealing with strings and text. But generally, regex is considered to be tough and people don't want to get their hands dirty with it. At least it was the case for me until I found &lt;a href="https://www.quora.com/What-are-the-best-resources-for-learning-regular-expressions?redirected_qid=4432534#!n=12"&gt;this&lt;/a&gt; Quora thread. The most upvoted answer in this quora question, in a way, provides a path to learn, understand and practice regex quickly and in a systematic manner. I have learnt from that path and I am going to share what I have learnt so that anyone can find all the information in a single place.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, What is Regex?
&lt;/h2&gt;

&lt;p&gt;Regular expressions are the group of characters which represent a pattern to be matched by strings. What this means is that usually, text documents like log files and phone book etc. contain many characters, words and we may want to extract meaningful information from those strings. Regular expressions come into play here. Regular expressions filter out specific data from given strings according to your needs and its up to you what you want to do with those extracted characters.&lt;br&gt;
 For example, in many web applications which require a user to sign up into their website, while creating password there may be some constraints as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;password should:
contain at least 6 alphanumeric characters,
start with an uppercase letter, 
end with at least one special symbol(*,@,# etc.)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In such case we can create a pattern through regular expressions as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'^[A-Z]\w{5}.*[*@#]$'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What above symbols mean is all about regex. So, There is no need to worry at this point. if you enter password violating above rules, your password won't match with regular expression and password cannot be created as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a password: 1223as //--&amp;gt;password cannot be created!!
Create a password: A123bc@# //--&amp;gt;password created!!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The regex written above defined some pattern and if any string did not match that pattern it did not allow password creation. Likewise, there can be many uses of regex to filter out text and extract specific information from text and do whatever we desire to do with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with Regex(using Python3)
&lt;/h2&gt;

&lt;p&gt;First things first, regex is generally used with some programming languages to operate on text from given text file or log file etc. We are going to use python to work with regex. One needs to be a little familiar with python syntax, the concept of modules and OOP to work with it. To use regular expressions in python we need to import module re. re module has many methods which facilitate working with regex. We will quickly look at the most important methods with which can perform almost every actions related to regex. While explaining these methods I will use regex in its simplest terms, that is, without any metacharacter. Metacharacters are special characters which are used in regular expressions. It will make us realize that regular expressions are string types object and not an only complicated piece of symbols that usually beginners think they are. In the simplest form, they can be used as simple string objects as we will see. Main methods that we are going to look at are:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. re.search()
&lt;/h3&gt;

&lt;p&gt;This, in my view, is the simplest and best way to work with regex in python. In fact, if done wisely, you can do almost every kind of string filtering and searching with this function alone. This method has the following signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;re.search(pattern,string,flags)
where the pattern is the regex written by you
the string is a string with which we want to match our pattern
flags are optional third arguments used for different purposes briefly discussed below
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see how this works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coding&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dev is the best place to learn coding.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dev is the best place to learn coding.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can run above code and see the result yourself but for reference the output of the above code is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
_sre.SRE_Match object; span=(31, 37), match='coding'
None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;re.search() returns what is known as match object as shown above, if the regex matches with the string. if the regex match object is returned it indicates that pattern was matched with string and if no such pattern is present we get 'None' as a result. If we want to see the content of the match object we can use the group() method.So, replace print(res) statement in above code with print(res.group()). Try it on IDE yourself and the output will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
Runtime Errors:
Traceback (most recent call last):
  File "/home/f79b34f7380e5d299ca6998dec4c6fef.py", line 6, in 
    print(res.group())
AttributeError: 'NoneType' object has no attribute 'group'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A runtime error occurs as "None" has no content and we are trying to access it. We will see more of search() and group() later.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. re.match()
&lt;/h3&gt;

&lt;p&gt;re.match() is a python specific method of re which is similar to search() except that it returns match object or in other words, it matches the pattern with string only if the given pattern is present at the beginning of the string. So, it is a special case of search() in which we are looking for a pattern only at the beginning of the string. Following example clarifies everything&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# match() method of re module
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China is the most populous country in the world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Most populous country in the world is China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#prints Match object as China is at the beginning of s2.
&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#prints None as China is not at the beginning of s2.
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="c1"&gt;# another way to use search() and match()
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;search possible in s1 and s2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above code shows another way to use search() and match() methods. If search happens and pattern is matched we can do something based on that.&lt;/p&gt;

&lt;h4&gt;
  
  
  matching start and end
&lt;/h4&gt;

&lt;p&gt;We saw that match() method returns match object only when the pattern occurs at the beginning of the test string. However, we have another way to achieve the same thing and that is with metacharacter '^'. If we want our pattern to occur at the beginning of the test string only then we can use ^ at the beginning of pattern as shown below. We use '^' with search() method. Its similarity with the match is shown below. Instead of using the match we can use search as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# replacing match using search and ^
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China is the most populous country in the world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Most populous country in the world is China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#prints Match object as China is at the beginning of the s1.
&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#prints None as China is not at the beginning of the s2.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
&amp;lt;_sre.SRE_Match object; span=(0, 5), match='China'&amp;gt;
None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, we have '$' metacharacter which is placed at the end of regex and the pattern matches only if given pattern is present at the end of the test string. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Using $ to match last characterset in the test string
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China is the most populous country in the world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Most populous country in the world is China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China$&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#prints None as China is not at the end of s1.
&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China$&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#prints Match object as China is at the end of s2.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;results are just reverse now as s2 has 'China' at the end of the sentence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
None
&amp;lt;_sre.SRE_Match object; span=(38, 43), match='China'&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we combine both '^' and '$' in a pattern the test string will match if it contains whatever is between '^' and '$' and nothing else. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China is the most populous country in the world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Most populous country in the world is China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;China&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^China$&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#prints None as s1 does not start and end with 'China'.
&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^China$&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#prints None as s2 does not start and end with 'China'.
&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^China$&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#prints Match object as test string only contains "China" and nothing else.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
None
None
&amp;lt;_sre.SRE_Match object; span=(0, 5), match='China'&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. re.findall()
&lt;/h3&gt;

&lt;p&gt;search() and match() methods only return a single matched substring in the form of the match object. But there can be many cases in which more than one substrings in the given string may match the regex pattern for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.at&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;the cat was chasing rat while the bat was looking at them&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where '.' is a metacharacter which represents any character accept newline. Until now we have used simple ascii characters with regex but regex has many metacharacters too which allows us to create patterns which can do more powerful things. We will see all essential metacharacters in regex later. In the above case, only cat is recorded so the output will be 'cat'. But what if we want such words, then either we can use loops with search() or we can use findall() as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.at&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;the cat was chasing rat while bat was looking at them&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
['cat', 'rat', 'bat', ' at']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, findall() returns the list of all non-overlapping matches of the patterns. &lt;/p&gt;

&lt;h3&gt;
  
  
  4. re.compile()
&lt;/h3&gt;

&lt;p&gt;With re.compile(), we can use the regex pattern again and again in our code. The signature of re.compile is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;re.compile(pattern,flags)
flags are optional arguments which we will see later.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the compile() returns an object known as regex object which has its own search(), match(),findall() and other methods that generally a match object has. So, below is the code which shows how compile() works and alternative way to work without compile() is also shown. There is no big difference between the two.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# compile method of re
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;comp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pattern&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;#comp is a regex object not a match object
&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;comp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;the pattern is present in this sentence.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#res is match object
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pattern&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;the pattern is present in this sentence.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#alternate way without compile, here res is match object
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
_sre.SRE_Match object; span=(4, 11), match='pattern' pattern
_sre.SRE_Match object; span=(4, 11), match='pattern' pattern
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  optional flag argument in re methods
&lt;/h4&gt;

&lt;p&gt;There is an optional flag argument in all re pattern matching methods explained below like search(), match(),findall() and compile().&lt;br&gt;
The flags can change the way regular expressions function. For that we will see one example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Noun&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Noun is the naming word. We use noun to name objects&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
['Noun', 'noun']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without 3rd argument it matches only one instance of 'Noun' but when we use re.IGNORECASE(also re.I) it ignores the case and matches both instance of 'noun'. Likewise there are other flag values also. If you want to learn more about them you can refer this &lt;a href="https://docs.python.org/3/library/re.html"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Syntax of Regular expressions
&lt;/h2&gt;

&lt;p&gt;Now that we have seen how to work with regex in python using the search(), match() and findall(), let's dive deep into the syntax of regular expressions. It is called syntax of regular expressions because regex contains many metacharacters having their own meaning which makes regex very powerful tool to extract and filter information from text data. Until now we have seen how regex can be used in python but only using simple strings. Regex is rarely used that way. Actually regex comes with many metacharacters as described below one by one.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Any Charatcer(.)
&lt;/h3&gt;

&lt;p&gt;The '.' is a metacharacter which matches anything in the test string(string in which we are looking for patterns) except a newline. As we saw before in example of findall() above we use '.' when we do not know beforehand which character we want to search for, for example, let the test string be&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"the house no. is 74-3B."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and we are required to look if there is a house no. according to the number rule that says that- the house no. contains two characters followed by a '-' and that followed by two characters then we can write a regex as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"..-.."//matches test string mentioned above
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As any character can occupy place around '-' we used '.'&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Character Class([])
&lt;/h3&gt;

&lt;p&gt;When we write [] in regex it represents character class and it matches only one out of several characters inside the square brackets. If we put '^' inside the square brackets before all the characters then it matches any character that is not in square brackets. if we put a hyphen(-) inside square brackets we can use it to match the range of characters in some sequence. The following list of examples will clarify these:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character Class Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[aA]&lt;/td&gt;
&lt;td&gt;matches 'a' or 'A'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[a-z]&lt;/td&gt;
&lt;td&gt;matches any of the characters ‘a’,’b’,’c’, ’d’…. or ‘z’&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[a-zA-Z]&lt;/td&gt;
&lt;td&gt;matches one character which lies in range ‘a’ to ‘z’ or ‘A’ to ‘Z’.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[-abc]&lt;/td&gt;
&lt;td&gt;matches ‘-‘ or ‘a’ or ‘b’ or ‘c’ i.e putting ‘-‘ as a standalone character has no special meaning.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[^ab]&lt;/td&gt;
&lt;td&gt;matches anything but ‘a’ or ‘b’.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[^a-z]&lt;/td&gt;
&lt;td&gt;matches anything but a character in the range a-z.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[a^b]&lt;/td&gt;
&lt;td&gt;matches ‘a’ or ‘^’ or ‘b’ i.e putting ‘^’ at any place apart from the first place inside square brackets makes it like any other standalone characters.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Predefined Character Classes
&lt;/h3&gt;

&lt;p&gt;With character classes, we can create any custom character class as we desire but regex also comes with some predefined character classes ready to use as mentioned below:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character Class Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;\d&lt;/td&gt;
&lt;td&gt;matches any numerical digit.Equivalent to [0-9].&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\D&lt;/td&gt;
&lt;td&gt;matches any non-digit character. it is a complement to \d.Equivalent to [^0-9].&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\s&lt;/td&gt;
&lt;td&gt;matches any white space character.Equivalent to [\t\n\r\f\v].&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\S&lt;/td&gt;
&lt;td&gt;matches any character which is not white space.equivalent to [^\t\n\r\f\v].&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\w&lt;/td&gt;
&lt;td&gt;matches any alphanumeric character also called as a word character.Equivalent to[a-zA-Z0-9_] plus characters defined in current locale.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\W&lt;/td&gt;
&lt;td&gt;matches anything but alphanumeric characters. Matches non-word characters.Equivalent to [^a-zA-Z0-9_]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\b&lt;/td&gt;
&lt;td&gt;matches empty strings at the beginning and end of a word.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\B&lt;/td&gt;
&lt;td&gt;matches empty strings but not at the beginning and end of a word.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\\&lt;/td&gt;
&lt;td&gt;matches backslash(‘\’) character.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\.&lt;/td&gt;
&lt;td&gt;matches period(‘.’) character.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4. Quantifiers
&lt;/h3&gt;

&lt;p&gt;Quantifiers are very important metacharacters in the regex. As their name suggests, they are used to repeat characters the desired number of times. Quantifiers are always used after some character or group of characters and define how many times that character will repeat. There are basically 4 types of quantifiers that we will see.&lt;/p&gt;

&lt;h4&gt;
  
  
  a. ?(for optional preceding character)
&lt;/h4&gt;

&lt;p&gt;It makes the preceding character or group of characters optional. for example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;army?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the arms and ammunitions should be provided to the army for security&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  b. *(zero or more repetitions)
&lt;/h4&gt;

&lt;p&gt;It is used if we want the preceding character or group of characters to repeat zero or more times. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ai*m&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;am aim aiim aiiims ai&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
['am', 'aim', 'aiim', 'aiiim']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above code shows that regex matches if there is an arbitrary number of i's between a and m.&lt;/p&gt;

&lt;h4&gt;
  
  
  c. +(one or more repetitions)
&lt;/h4&gt;

&lt;p&gt;It matches only if the preceding character to it occurs at least one or more times. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ai+m&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;am aim aiim aiiims ai&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
['aim', 'aiim', 'aiiim']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that for same test string, only those strings match which have at least one 'i' betweeb 'a' and 'm'.&lt;/p&gt;

&lt;h4&gt;
  
  
  d. {x}(fixed no. of repetitions)
&lt;/h4&gt;

&lt;p&gt;Using + and * is nice but when we use them the preceding characters can repeat any no. of times. In some situations we want some characters or group of characters to repeat exactly desired no. of times. This can be achieved using {x} which matches only if preceding characters are repeated x no. of times. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Enter your phone no. it must be of 10 digits!!!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\d{10}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Incorrect no. of digits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user enters anything less than or more than 10 digits above he or she will receive the message as "Incorrect no. of digits". Output when the phone no. is valid and invalid is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#valid
Output:
Enter your phone no. it must be of 10 digits!!!
9999999999
correct
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#invalid
Output:
Enter your phone no. it must be of 10 digits!!!
100
Incorrect no. of digits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  e. {x,y}(fixed and ranged repetitions):
&lt;/h4&gt;

&lt;p&gt;In the above example, we can see that 100 is evaluated as invalid because of our code. But 100 is infact, a valid number. So, sometimes we may want restrictions in range. Then we use {x,y} which matches only if preceding characters repeat at least x times and at most y times. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Enter your phone no. it should be between 3 to 10 digits!!!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\d{3,10}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Incorrect no. of digits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
Enter your phone no. it should be between 3 to 10 digits!!!
100
correct
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we write {, y } it means 0 to y no. of preceding characters are allowed and if we write {x, } it means at least x characters and more no. of characters are allowed.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Grouping and Capturing
&lt;/h3&gt;

&lt;p&gt;Next piece of syntax in regex involves groups and capturing groups. Grouping is one of the best features of regex. We can create a group by wrapping our regular expressions around parentheses (). If we use quantifiers after a group, it applies to the whole group and not to a single character. Let's understand grouping with an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Enter your phone no. it must be of 10 digits!!!. ISD code is optional.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(\+\d{2})?\d{10}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Incorrect no. of digits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
Enter your phone no. it must be of 10 digits!!!. ISD code is optional.
9999999999
correct
Enter your phone no. it must be of 10 digits!!!. ISD code is optional.
+919999999999
correct
Enter your phone no. it must be of 10 digits!!!. ISD code is optional.
988
Incorrect no. of digits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above example shows how groups work in the regex. To make the ISD code optional we could have used '?' operator character by character but groups make it very convenient as it treats all these characters as one unit. We grouped the characters and made it optional by applying '?' operator at once. However, this is not all that we can do with groups. we can capture groups and use them later in our programs. This makes regex very powerful. We can extract and desired strings and pattern and do some processing with them.&lt;/p&gt;

&lt;p&gt;Before looking at how capturing is done lets first see another method that re module provides us.&lt;/p&gt;

&lt;h4&gt;
  
  
  group()
&lt;/h4&gt;

&lt;p&gt;Until now we have been using match objects with if statements only but what if we want to see what characters were actually matched by the given test string? For this purpose, we use the group() method which was discussed briefly before. The group() method is applied on match object returned by search() or match() as discussed above. Let's see an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coding&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dev is the best place to learn coding.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
&amp;lt;_sre.SRE_Match object; span=(31, 37), match='coding'&amp;gt;;
coding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the above output, we can see that when group method is used in it's simplest form without any arguments it simply returns the characters that were matched in a match object. When we try to print res itself as it is a match object we get &lt;code&gt;&amp;lt;_sre.SRE_Match object; span=(31, 37), match='coding'&amp;gt;&lt;/code&gt; as output. But when we print res.group() we get the actual content of the match object that was matched.&lt;/p&gt;

&lt;h4&gt;
  
  
  Alternative matching in groups
&lt;/h4&gt;

&lt;p&gt;We can use or operator with groups as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;regex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(Tom|Dick|Harry)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Tom is in the pattern&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;res2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Dick is in the pattern&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;res3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Harry is in the pattern&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;res4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Sal is in the pattern&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
&amp;lt;_sre.SRE_Match object; span=(0, 3), match='Tom'&amp;gt;
&amp;lt;_sre.SRE_Match object; span=(0, 4), match='Dick'&amp;gt;
&amp;lt;_sre.SRE_Match object; span=(0, 5), match='Harry'&amp;gt;
None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From above example we can see that any pattern from the group matches.&lt;/p&gt;

&lt;h4&gt;
  
  
  Capturing and backreferences
&lt;/h4&gt;

&lt;p&gt;With a group() we can capture expressions as shown above. It is capturing because it helps us to extract characters from matched objects which is useful as we will see. But the better use of group comes with arguments if we wrap some specific expression part inside a group(using paranthesis) we can get the content of the group using numbers as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(cod)ing&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gfg is the best place to learn coding.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
&amp;lt;_sre.SRE_Match object; span=(31, 37), match='coding'&amp;gt;
coding
cod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see after wrapping the character set 'cod' in paranthesis we formed the group (cod). So, to get all the matched characters we simply type res.group() and to get the contents of the group (cod) we type res.group(1). One natural question that arises is that why did we use 1 as an argument and nothing else? It will be clear when we will use multiple groups in the same regular expression. This is, in fact, another use of the group. To group different parts of expression and use them in our program conveniently. A simple example is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(cod)(ing)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gfg is the best place to learn coding.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
&amp;lt;_sre.SRE_Match object; span=(31, 37), match='coding'&amp;gt;
coding
cod
ing

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The use of number 1 and 2 as arguments becomes evident now. Numbers indicate the groups starting from left to right(leftmost group in the expression is assigned the value of 1 and then the value increases as we move towards right). And this process is what we call capturing. We can use groups to capture specific character sets and use them in our programs.&lt;br&gt;
To see how capturing is useful, we will see an example borrowed from &lt;a href="https://www.hackerrank.com/challenges/split-number/submissions/code/89169667"&gt;hackerrank&lt;/a&gt;. The problem is explained below:&lt;/p&gt;

&lt;p&gt;We have been given the list of phone numbers of the format&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Country code]-[Local Area Code]-[Number] 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our job as a regex expert is to split it into country code, local area code and number and display them distinctly as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;given number = 91-011-23413627
desired output after processing:
CountryCode=91,LocalAreaCode=011,Number=23413627
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some constraints are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The number of numbers, N is in the range 1&amp;lt;=N&amp;lt;=20.&lt;/li&gt;
&lt;li&gt;There might either be a '-' ( ascii value 45), or a ' ' ( space, ascii value 32) between the segments &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can solve above problem with the code below using groups and capturing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;())):&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;([0-9]{1,3})[- ]([0-9]{1,3})[- ]([0-9]{4,10})&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CountryCode={},LocalAreaCode={},Number={}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above uses many concepts that we have learnt until now. Let's see one by one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The first for..in loop is used because the user is asked how many numbers they want to process.&lt;/li&gt;
&lt;li&gt;For every number, we check whether the number string matches our pattern which must contain from 1 to 3 digits in the country code segment, from 1 to 3 digits in local area code segment and 4 to 10 digits in actual number segment.&lt;/li&gt;
&lt;li&gt;We use groups to capture each segment.&lt;/li&gt;
&lt;li&gt;We display each segment using the group method with arguments from 1 to 2 to 3 from left to right.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  6. Backreferences
&lt;/h3&gt;

&lt;p&gt;Backreferences are useful when we use the same text or character set again and again in the same expression. Whenever we create a group it is automatically stored in what we call as a backreference so that latter it can be used in program or expression itself. In fact, what we used as arguments in the group() method were actually backreferences. We have already seen one use of backreferences. That was to use captured groups in our program. Now we will see the use of backreferences inside our expressions. If we are looking for the same group of characters, again and again, we can use backreferences. Let's look at a regular expression for instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[A-Za-z]{3}\d{3}[A-Za-z]{3}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern simply requires that there should be 3 alphabetical characters followed by three numerical digits followed by 3 alphabetical letters again. But we don't need to write whole regex for alphabetical letters again and if that same regex is required more times it will become lengthier to write same regex again and again. Instead we can use backreferences by grouping the first occurence as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;([A-Za-z]){3}\d{3}\1{3}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;'\1' in the above example represents backreference of the group ([A-Za-z]) we can replace all the other occurrences of that group with stored backreference '\1'. Numbering is done the same as group method starting from 1 and increasing numbers from left to right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search and replace with re.sub() method
&lt;/h2&gt;

&lt;p&gt;The final method that we will see before closing this tutorial is sub() method of re module which helps us to search for specific text in the test string and replace it with the desired text. The signature of sub() method is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;re.sub(pattern, replacement, string)
where the pattern is the regex written by you
replacement is the string which we want to replace our pattern with
the string is a test string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One simple example is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;people in indie are called indiens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;indie&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;India&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
people in India are called Indians
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's it. This is the end of this somewhat long tutorial. I don't claim in any way that this tutorial is complete and I have covered everything that can be done with regex in python but basics are pretty much covered. After reading this tutorial I would recommend doing the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Read some more topics in detail like word boundaries(\b and \B), splitting with regular expressions, more flags. But apart from these, this tutorial has covered all the basics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Practice regex syntax from this &lt;a href="https://regexone.com/"&gt;site&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;practice using regex with python from &lt;a href="https://www.hackerrank.com/domains/regex"&gt;hackerrank regex track&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Follow these three steps after this tutorial and you will have decent enough knowledge to apply regex not only in python but any other language. As they say "practice makes permanent". &lt;/p&gt;

&lt;p&gt;Cheers!!&lt;/p&gt;

&lt;h5&gt;
  
  
  Credits(Cover Image):&lt;em&gt;Image by &lt;a href="https://pixabay.com/users/msandersmusic-1972097/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1181864"&gt;msandersmusic&lt;/a&gt; from &lt;a href="https://pixabay.com/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1181864"&gt;Pixabay&lt;/a&gt;&lt;/em&gt;
&lt;/h5&gt;

</description>
      <category>python</category>
      <category>regex</category>
      <category>computerscience</category>
    </item>
  </channel>
</rss>
