The asterisk in regular expressions
In a regular expression, * means "zero or more of the previous item". ab*c matches ac, abc and abbbc. To match an actual asterisk, escape it as \* or put it in a character class: [*].
What * repeats
The * applies only to the single item right before it: one character, one character class, or one group in parentheses.
| Pattern | Matches | Doesn't match |
|---|---|---|
a* |
an empty string, a, aaaa |
b as a whole |
ab* |
a, ab, abbb |
abab as a whole |
(ab)* |
an empty string, ab, abab |
aab as a whole |
[0-9]* |
an empty string, 7, 2026 |
20x6 as a whole |
.* |
anything on one line | a line break, unless the "dot matches newline" flag is on |
The "zero" catches people out
Because * allows zero repeats, a pattern built from it can match nothing at all, and it happily does:
import re
print(re.findall(r"\d*", "a1")) # ['', '1', '']
print(bool(re.fullmatch(r"\d*", ""))) # True: an empty string is "zero digits"
If you need at least one, use + instead: \d+. A form field validated with ^\d*$ accepts an empty value, which is rarely what anyone meant.
Greedy and lazy: * versus *?
By default * is greedy: it grabs as much as it can and only gives back what the rest of the pattern needs. Adding a ? makes it lazy, so it takes as little as possible.
const html = "<b>bold</b>";
console.log(html.match(/<.*>/g)); // [ '<b>bold</b>' ]
console.log(html.match(/<.*?>/g)); // [ '<b>', '</b>' ]
Java, PCRE, .NET and Python 3.11 and later also support a possessive form, *+, which never gives anything back. It's useful for stopping runaway backtracking on long inputs.
Matching a literal asterisk
| Where | Write | Notes |
|---|---|---|
| Any regex | \* |
The backslash makes it an ordinary character |
| Any regex | [*] |
Inside square brackets, * is already literal |
| Python | r"\*" |
Use a raw string so Python leaves the backslash alone |
| JavaScript literal | /\*/ |
|
| JavaScript string | new RegExp("\\*") |
The string needs a doubled backslash |
| Escaping user input | re.escape(s) in Python, RegExp.escape(s) in JavaScript |
Escapes * and every other special character |
RegExp.escape is new: it arrived with ES2025 and works in Node 24 and current browsers. In older environments, s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") does the same job.
grep and sed
In grep, sed and other POSIX tools, * means the same thing in both basic and extended syntax, and \* still matches a real asterisk. One quirk of basic syntax: a * at the very start of a pattern has nothing to repeat, so it's treated as a literal. grep '*note' file.txt finds "*note", while grep -E '*note' file.txt stops with an error. Don't rely on either. Write grep '\*note' and the intent is clear to everyone, including other regex engines.
Quote the pattern in the shell, or the shell may expand the * into file names before grep ever sees it. See the asterisk in Bash.
Regex * is not a glob *
In a file pattern like *.txt, * means "any characters". In a regex it means "repeat the previous thing", and a pattern can't start with it:
import re
re.compile("*.txt") # re.error: nothing to repeat at position 0
The regex version of the glob *.txt is ^.*\.txt$. The glob * is the regex .*, and the glob ? is the regex ..
Where the name comes from
The mathematician Stephen Kleene described this operator in the 1950s while working on what became regular languages. It's still called the Kleene star, and in theory books Σ* means "every string you can make from the alphabet Σ", including the empty one.