g | x | w | all
Bytes Lang Time Link
043JavaScript V8250519T123831ZSteve Be
018Japt250519T081359ZShaggy
051Swift 6240317T005927ZmacOSist
012Vyxal241204T225436Zlyxal
041AWK241204T211832Zxrs
045Scala 3240317T022326Z138 Aspe
102C gcc171121T220846Zgastropn
063Python 3171125T153020ZJonathan
077Ruby180622T160740ZAlex All
045Z80 Assembly171123T022509Zintrospe
025APL Dyalog Unicode180409T124247ZErik the
181Excel180409T112036ZWernisch
141C 168171120T230621ZPrincePo
099C171122T105042ZToby Spe
102C gcc171120T200731ZMichael
432Matlab171126T210535ZJeremiah
069C gcc171122T103100Zjxh
021Jelly171125T131143ZJonathan
022Deorst171123T203643Zcaird co
085Python 3 no regex171123T184111ZAlex
081Haskell171123T155815ZLaikoni
020QuadS171122T225959ZUriel
039Jelly171120T215331ZAdalynn
023Retina171120T171653ZNeil
026Perl5171122T174208ZKjetil S
033Ruby171122T153140ZG B
035GNU sed171122T122424ZToby Spe
076Befunge171120T214200ZJames Ho
043Java OpenJDK 8171121T101707ZLuca H
nan171121T210534Zdjhurio
035J171120T235347ZFrownyFr
041JavaScript171120T235527ZForty3
051q/kdb+171121T090058Zmkst
nan171120T232234ZBrad Gil
125Mathematica171120T220110ZZaMoC
036Jelly171120T205851Zlynn
027Pip171120T202005ZDLosc
044Jelly171120T175051ZErik the
023Pyth171120T171733ZMr. Xcod
020Retina171120T172106ZUriel
050Python 2171120T170512ZErik the
028Retina171120T171253Zovs

JavaScript (V8), 43 bytes

s=>s.replace(/(one|th|tw|f|s|.i|z)/g,',$1')

Try it online!

JavaScript (V8), 46 bytes

s=>s.replace(/(one|tw|th|f|s|ei|ni|z)/g,',$1')

Try it online!

JavaScript (V8), 51 bytes

s=>s.replace(/(ne|wo|ee|ur|ive|x|en|ht|ro)/g,'$1,')

Try it online!

Japt, 18 bytes

f.²³i2`[eox]|[ t]?

Try it

Swift 6, 76 69 51 bytes

{($0+"").replacing(/one|t[wh]|.i|[fsz]/){" "+$0.0}}

Try it on SwiftFiddle!

The output includes an extra leading space.

Vyxal, 12 bytes

øṖ'ƛ9ʀ∆ċ$c;A

Try it Online!

The joys of having a number to words built-in. Where we're going we don't need regex

Explained

øṖ'ƛ9ʀ∆ċ$c;A­⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁤‏⁠‎⁡⁠⁣⁣‏⁠‎⁡⁠⁣⁤‏‏​⁡⁠⁡‌⁣​‎⁠‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏‏​⁡⁠⁡‌⁤​‎⁠⁠‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏‏​⁡⁠⁡‌­
øṖ'           # ‎⁡Keep partitions of the input where:
   ƛ      ;A  # ‎⁢  All strings in the partition:
        $c    # ‎⁣    Are cornained within
      ∆ċ      # ‎⁤    Number to wordification of
    9ʀ        # ‎⁢⁡    The range [0, 9]
💎

Created with the help of Luminespire.

AWK, 41 bytes

gsub(/([ots]|[zfn].|(se|th|ei).)../," &")

Attempt This Online!

Scala 3, 45 bytes

Use Regular Expression (scala.util.matching.Regex)


Golfed version. Attempt This Online!

"""..[eox]|[tse]?....""".r.findAllIn(_).toSeq

Ungolfed version. Attempt This Online!

import scala.util.matching.Regex

object Main {
  def main(args: Array[String]): Unit = {
    val pattern: Regex = """..[eox]|[tse]?....""".r

    val input = "threesevensevensixninenineninefiveeighttwofiveeightsixthreeeight"
    val matches = pattern.findAllIn(input).toList

    println(matches.mkString(", "))
  }
}

C (gcc), 106 bytes 104 102 bytes

-2 bytes thanks to @jxh -2 bytes thanks to ceilingcat

c;f(char*s){for(char*t=" $&=B*,29/?";*s;)for(c=4+(index(t,(*s^s[1])+35)-t)/4;c--;)putchar(c?*s++:32);}

Try it online!

XOR is truly our greatest ally.

Python 3, no regex,  83 68 65  63 bytes

-15 thanks to Lynn (refactor into a single function)
-3 more thanks to Lynn (avoid indexing into a list with more arithmetic)
...leading to another save of 2 bytes (avoiding parenthesis with negative modulos) :)

def f(s):h=ord(s[0])*ord(s[1])%83%-7%-3+5;print(s[:h]);f(s[h:])

A function which prints the words separated by newlines and then raises an IndexError.

Try it online! (suppresses the exceptions to allow multiple runs within the test-suite)

Ruby, 77 bytes

puts gets.gsub(/(one|two|three|four|five|six|seven|eight|nine)/,' \1 ').strip

Try it online!

Z80 Assembly, 46 45 bytes

; HL is the address of a zero-terminated input string
; DE is the address of the output buffer

Match5: ldi                                 ; copy remaining characters
Match4: ldi
Match3: ld a,32 : ld (de),a : inc de        ; and add space after a matched word.

Uncollapse:

        ld a,(hl) : ldi : or a : ret z      ; copy first byte (finish if it was zero)
        ex af,af'                           ; and save its value for later.

        ldi : ld a,(hl) : ldi               ; copy second and third bytes

        cp 'e' : jr z,Match3                ; is the third letter 'e' or 'o' or 'x'?
        cp 'o' : jr z,Match3
        cp 'x' : jr z,Match3

        ex af,af'                           ; now look at the first letter

        cp 'e' : jr z,Match5                ; is it 't' or 's' or 'e'?
        sub 's' : jr z,Match5
        dec a : jr z,Match5
        jr Match4

(It was fun to adapt the Uriel's cool regex to a regex-unfriendly environment).

APL (Dyalog Unicode), 25 bytes

'..[eox]|[tse]?....'⎕S'&'

Try it online!

Excel, 181 bytes

=SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A1,"z"," z"),"on"," on"),"tw"," tw"),"th"," th"),"f"," f"),"s"," s"),"ei"," ei"),"ni"," ni")

Places a space in front of: z, on, tw, th, f, s, ei, ni

C 168 ,145,144,141 bytes

EDIT: Tried init 'i' to 1 like so

a,b;main(i)

To get rid of leading whitespace,
but it breaks on input starting with three, seven or eight

141

#define s|a%1000==
a,i;main(b){for(;~scanf("%c",&b);printf(" %c"+!!i,b),a|=b%32<<5*i++)if(i>4|a%100==83 s 138 s 116 s 814 s 662 s 478)a=i=0;}

Try it online

144

a,i;main(b){for(;~(b=getchar());printf(" %c"+!!i,b),a=a*21+b-100,++i)if(i>4|a==204488|a==5062|a==7466|a==23744|a==21106|a==6740|a==95026)a=i=0;}

Try it online

168

i,a;main(b){for(;~scanf("%c",&b);printf(" %c"+!!i,b),a|=b<<8*i++)if(i>4|a==1869768058|a==6647407|a==7305076|a==1920298854|a==1702259046|a==7891315|a==1701734766)a=i=0;}

Try it online!

Ungolfed

i,a;main(b){
for(;~scanf("%c",&b); // for every char of input
printf(" %c"+!!i,b), // print whitespace if i==0 , + char
a|=b<<8*i++ // add char to a for test
)
if(
i>4| // three seven eight
a==1869768058|      // zero
a==6647407|        // one
a==7305076|       // two
a==1920298854|   //four
a==1702259046|  //five
a==7891315|    //six
a==1701734766 //nine
) a=i=0; //reset i and a
}

int constants gets unnecessary large by shifting a<<8
but in case you can compare to strings somehow it should be the most natural

146 Using string comparison

#define s|a==*(int*)
a,b;main(i){for(;~(b=getchar());printf(" %c"+!!i,b),a|=b<<8*i++)if(i>4 s"zero"s"one"s"two"s"four"s"five"s"six"s"nine")a=i=0;}

Using String comparison

Obfuscated

#define F(x)if(scanf(#x+B,&A)>0){printf(#x,&A);continue;}
B;A;main(i){for(;i;){B=1;F(\40e%4s)F(\40th%3s)F(\40se%3s)F(\40o%2s)B=2;F(\40tw%1s)F(\40si%1s)B=1;F(\40%4s)i=0;}}

C, 103 99 bytes

char*r="f.tzuonresn.xgv";f(char*s){*s&&f(s+printf("%.*s ",(strrchr(r,s[2])-strchr(r,*s))%10,s)-1);}

This works for any character encoding (including awkward ones like EBCDIC), because it doesn't use the numeric value of the input characters. Instead, it locates the first and third letters in a magic string. The distance between these indicates how many letters to advance with each print.

Test program

#include <stdio.h>
int main(int argc, char **argv)
{
    for (int i = 1;  i < argc;  ++i) {
        f(argv[i]);
        puts("");
    }
}

C (gcc), 179 159 146 139 137 116 107 103 102 bytes

Edit 1: (Added suggestions from Mr. Xcoder - thanks! - My macro version was same size as yours, but I like yours better.)

Edit 2: Changed char individual compares to calls to strchr()

Edit 3: K&R's the var declarations (Eww!)

Edit 4: When 1 macro is not enough...

Edit 5: Redone with new algorithm suggested above. Thanks to James Holderness for this great idea!

Edit 6: Removed 0 set as it seems to go there automatically - Master level code golf techniques used (commas, printf trick, etc.) - thanks gastropner!

Edit 7: Use memchr and fixed a bug pointed out by James Holderness.

Edit 7: Use && on final check to replace ? - thanks jxh.

c,h;f(char*s){while(c=*s++)putchar(c),h=h%10816*104+c%27,memchr("&;@X\\ru{",h%3817,9)&&putchar(h=32);}

Try it online!

Non-golfed (Which is still very golfy honestly...)


int c;
int h;
void f(char*s)
{
    while(c=*s++)
        putchar(c),
        h=h%10816*104+c%27,
        memchr("&;@X\\ru{",h%3817,9)?putchar(h=32):1;
}

Old, straight forward grep-esqe solution:

#define p putchar
#define q c=*s++
c,x;f(char*s){while(q){p(c);x=strchr("tse",c);p(q);p(q);if(!strchr("eox",c)){p(q);if(x)p(q);}p(' ');}}

Old, cleaner version.

// Above code makes a macro of putchar() call.

void f(char *s)
{
    char c;
    while(c = *s++)
    {
        putchar(c);
        int x = strchr("tse", c);

        putchar(*s++);
        putchar(c=*s++);

        if(!strchr("eox", c))
        {
            putchar(*s++);
            if(x)
            {
                putchar(*s++);
            }
        }       
        putchar(' ');
    }
}

Try it online!

Matlab, 432 bytes

A long attempt.

s=char(input('','s'));o=[];while length(s)>0;switch s(1:2);case'ze';o=[o,' ',s(1:4)];s(1:4)=[];case'on';o=[o,' ',s(1:3)];s(1:3)=[];case'tw';o=[o,' ',s(1:3)];s(1:3)=[];case 'th';o=[o,' ',s(1:5)];s(1:5)=[];case'fo';o=[o,' ',s(1:4)];s(1:4)=[];case'fi';o=[o,' ',s(1:4)];s(1:4)=[];case 'si';o=[o,' ',s(1:3)];s(1:3)=[];case'se';o=[o,' ',s(1:5)];s(1:5)=[];case 'ei';o=[o,' ',s(1:5)];s(1:5)=[];otherwise;o=[o,' ',s(1:4)];s(1:4)=[];end;end;o

ungolfed:

s=char(input('','s'))
o=[]
while length(s)>0
switch s(1:2)
    case 'ze'
        o=[o,' ',s(1:4)]
        s(1:4)=[]
    case 'on'
        o=[o,' ',s(1:3)]
        s(1:3)=[]
    case 'tw'
        o=[o,' ',s(1:3)]
        s(1:3)=[]
    case 'th'
        o=[o,' ',s(1:5)]
        s(1:5)=[]
    case 'fo'
        o=[o,' ',s(1:4)]
        s(1:4)=[]
    case 'fi'
        o=[o,' ',s(1:4)]
        s(1:4)=[]
    case 'si'
        o=[o,' ',s(1:3)]
        s(1:3)=[]
    case 'se'
        o=[o,' ',s(1:5)]
        s(1:5)=[]
    case 'ei'
        o=[o,' ',s(1:5)]
        s(1:5)=[]
    otherwise
        o=[o,' ',s(1:4)]
        s(1:4)=[]
end
end
o

C (gcc), 89 80 76 75 72 71 70 69 bytes

f(char*s){*s&&f(s+printf(" %.*s",""[(*s^s[2])%12],s)-1);}

Try it online!

(89) Credit to gastropner for the XOR hash.
(76) Credit to Toby Speight for the idea of using 1st and 3rd.
(75) Credit to Michael Dorgan for '0'48.
(72) Credit to Michael Dorgan and Lynn for literals with control characters.
(69) Credit to Lynn for x?y:0x&&y

f (char *s) {        /* K&R style implicit return type. s is the input. */
    *s&&f(           /* Recurse while there is input. */
        s+printf(    /* printf returns the number of characters emitted. */
            " %.*s", /* Prefix each digit string with a space. Limit
                      * how many bytes from the string to print out. */
            ""
                     /* Magic hash table, where the value represents
                      * the length of the digit string. The string
                      * is logically equivalent to
                      * "\04\01\05\03\04\05\05\04\04\01\03\03" */
            [(*s^s[2])%12],
                     /* The XOR hash (mod 12) */
            s)       /* The current digit. */
            -1);}    /* Subtract 1 for the space. */

Jelly,  23  21 bytes

ḣ3OP%953%7%3+3ɓḣṄȧṫḊÇ

A full program printing line-feed separated output. Note: once it's done it repeatedly prints empty lines "forever" (until a huge recursion limit or a seg-fault)

Try it online! (TIO output is accumulated, a local implementation will print line by line)

How?

Starting with a list of characters, the program repeatedly:

  1. finds the length of the first word of the list of characters using some ordinal mathematics;
  2. prints the word plus a linefeed; and
  3. removes the word from the head of the list of characters

The length of the first word is decided by inspecting the first three characters of the current list of characters (necessarily part of the first word). The program converts these to ordinals, multiplies them together, modulos the result by 953, modulos that by seven, modulos that by three and adds three:

word   head3  ordinals       product  %953  %7  %3  +3 (=len(word))
zero   zer    [122,101,114]  1404708   939   1   1   4
two    two    [111,110,101]  1233210    28   0   0   3
one    one    [116,119,111]  1532244   773   3   0   3
three  thr    [116,104,114]  1375296   117   5   2   5
four   fou    [102,111,117]  1324674     4   4   1   4
five   fiv    [102,105,118]  1263780   102   4   1   4
six    six    [115,105,120]  1449000   440   6   0   3
seven  sev    [115,101,118]  1370570   156   2   2   5
eight  eig    [101,105,103]  1092315   177   2   2   5
nine   nin    [110,105,110]  1270500   151   4   1   4

ḣ3OP%953%7%3+3ɓḣṄȧṫḊÇ - Main link, list of characters           e.g. "fiveeight..."
ḣ3              - head to index three                                "fiv"
  O             - ordinals                                           [102,105,118]
   P            - product                                            1263780
    %953        - modulo by 953                                      102
        %7      - modulo by seven                                    4
          %3    - modulo by three                                    1
            +3  - add three                                          4

              ɓ - dyadic chain separation swapping arguments...
... ḣṄȧṫḊÇ ...
    ḣ         - head to index                                        "five"
     Ṅ        - print the result plus a line-feed and yield the result
       ṫ      - tail from index                                      "eeight..."
      ȧ       - and (non-vectorising)                                "eeight..."
        Ḋ     - dequeue                                               "eight..."
         Ç    - call the last link (Main*) as a monad with this as input
              -       * since it's the only link and link indexing is modular.

Deorst, 22 bytes

'..[eox]|[tse]?....'gf

Try it online!

Of course, this uses the regex found by Uriel. Although, it’s great when Deorst beats Pyth and Jelly :P

Python 3 (no regex), 85 bytes

i=3
while i<len(s):
	if s[i-3:i]in'ineiveroneghtwoureesixven':s=s[:i]+' '+s[i:]
	i+=1

Try it online!

Haskell, 81 bytes

f[c]=[c]
f(h:t)=[' '|s<-words"z one tw th f s ei ni",and$zipWith(==)s$h:t]++h:f t

Try it online!

Explanation:

f(h:t)=                      h:f t -- recurse over input string
   [' '|s<-               ]++      -- and add a space for each string s
      words"z one tw th f s ei ni" -- from the list ["z","one","tw","th","f","s","ei","ni"]
      ,and$zipWith(==)s$h:t        -- which is a prefix of the current string

QuadS, 21 20 bytes

..[eox]|[tse]?....
&

Try it online!

This is a port of my retina answer.

Jelly, 40 39 bytes

“¢¤Ƙƒ⁺6j¹;Ċ-ḶṃżṃgɼṘƑUẏ{»Ḳe€€@ŒṖẠ€TḢịŒṖK

Try it online!

How it works

“¢¤Ƙƒ⁺6j¹;Ċ-ḶṃżṃgɼṘƑUẏ{»Ḳe€€@ŒṖẠ€TḢịŒṖK
“¢¤Ƙƒ⁺6j¹;Ċ-ḶṃżṃgɼṘƑUẏ{»                 = the compressed string of the digit names
                        Ḳ                = split at spaces
                         e€€@ŒṖ          = check whether each member of each partition of the argument is a digit.
                               Ạ€        = A function that checks whether all values of an array are true, applied to each element.
                                 T       = Finds the index of each truthy element 
                                  Ḣ      = Grab the first element, since we have a singleton array
                                    ịŒṖ  = The previous command gives us the index, partition that splits the input into digits. This undoes it and gives us the partition.
                                       K = Join the array of digits with spaces                

Retina, 24 23 bytes

!`..[eox]|[fnz]...|.{5}

Try it online! Edit: Saved 1 byte thanks to @FrownyFrog.

Perl5, 26 bytes

echo zeronineoneoneeighttwoseventhreesixfourtwofive \
| perl -ple 's/..[eox]|[tse]?..../$& /g'

Or just the program:

s/..[eox]|[tse]?..../$& /g

Ruby, 33 bytes

->s{s.scan(/..[eox]|[tse]?..../)}

Try it online!

(Everybody else is doing it, so why can't we?)

GNU sed, 35 bytes

(including +1 for the -r flag)

s/([ots]|[zfn].|(se|th|ei).)../ &/g

Just a simple regexp replacement.

Befunge, 87 85 81 76 bytes

<*"h"%*:"h"$_02g-v1$,*<v%*93,:_@#`0:~
"@{&ruX;\"00^ !: _>_48^>+:"yp!"*+%02p0

Try it online!

Befunge doesn't have any string manipulation instructions, so what we do is create a kind of hash of the last three characters encountered, as we're processing them.

This hash is essentially a three digit, base-104 number. Every time a new character is read, we mod the hash with 1042 to get rid of the oldest character, multiply it by 104 to make space for the new character, then add the ASCII value of the new character mod 27 (to make sure it doesn't overflow).

For comparison purposes, we take this value mod 3817, write it into memory (thus truncating it to 8 bits), which results in smaller numbers that are easier for Befunge to handle. The hashes we then have to compare against are 0, 38, 59, 64, 88, 92, 114, 117, and 123. If it matches any of those, we know we've encountered a character sequence that marks the end of a number, so we output an additional space and reset the hash to zero.

If you're wondering why base 104, or why mod 3817, those values were carefully chosen so that the hash list we needed to compare against could be represented in as few bytes as possible.

Java (OpenJDK 8), 55 46 43 bytes

Saving 9 bytes thanks to Forty3/FrownyFrog

Saving 3 bytes thanks to Titus

s->s.replaceAll("one|tw|th|f|z|s|.i"," $0")

Try it online!

edit: Thank you for the welcome and explanation of lambdas!

Quite long one. You are welcome to golf it down.

R, 109 bytes

function(x)for(i in utf8ToInt(x)){F=F+i;cat(intToUtf8(i),if(F%in%c(322,340,346,426,444,448,529,536,545))F=0)}

Try it online!

J, 37 35 bytes

rplc'twthsiseeinionzef'(;LF&,)\~_2:

Try it online!

JavaScript, 66 57 52 44 41 bytes

s=>s.replace(/one|t[wh]|.i|[fsz]/g," $&")

Pretty naive, but it works.

Nice catch by FrownyFrog to use 2 chars .. except for "one" which a pure 2 char check might mess up zeronine. Edit: the single f and s were good catches by FrownyFrog that I overlooked my first two golfs.

Thanks, Neil, for the suggestion of an unnamed lambda and being able to use a single char for z gets down to 52.

Titus comes up with a smaller RegEx. I feel we are heading toward Uriel's regex eventually.

q/kdb+, 59 51 bytes

Solution:

{asc[raze x ss/:string`z`one`tw`th`f`s`ei`ni]cut x}

Example:

q){asc[raze x ss/:string`z`one`tw`th`f`s`ei`ni]cut x}"threesevensevensixninenineninefiveeighttwofiveeightsixthreeeight"
"three"
"seven"
"seven"
"six"
"nine"
"nine"
"nine"
"five"
"eight"
"two"
"five"
"eight"
"six"
"three"
"eight"

Explanation:

Quick solution, probably better and more golfable approaches.

{asc[raze x ss/:string`z`one`tw`th`f`s`ei`ni]cut x} / ungolfed solution
{                                                 } / lambda with implicit x as input
                                             cut x  / cut x at indices given by left
 asc[                                       ]       / sort ascending
                string`z`one`tw`th`f`s`ei`ni        / string list ("z","one",...)
          x ss/:                                    / string-search left with each right
     raze                                           / reduce down list

Notes:

46 bytes with some simple golfing, replacing q calls with k ones, but still a hefty solution.

asc[(,/)x ss/:($)`z`one`tw`th`f`s`ei`ni]cut x:

Perl 6,  42  30 bytes

*.comb(/<{(0..9).Str.uninames.lc.words}>/)

Test it

{m:g/..<[eox]>||<[tse]>?..../}

Test it
(Translated from other answers)

Mathematica, 125 bytes

(s=#;While[StringLength@s>2,t=1;a="";While[FreeQ[IntegerName/@0~Range~9,a],a=s~StringTake~t++];Print@a;s=StringDrop[s,t-1]])&


Try it online!

TIO outputs an error message about "CountryData"(???)
I don't know why this happens, but eveything works fine on Mathematica

Jelly, 36 bytes

œṣj⁶;$}
W;“€ɗİẒmṫṃ¦¦ạỊɦ⁼Fḷeṭḷa»s2¤ç/

Try it online!

Algorithm:

for x in ['ze', 'ni', 'on', 'tw', 'th', ...]:
    replace x in input by space+x

I bet we can do even better.

Pip, 27 bytes

aR`[zfs]|one|[ent][iwh]`s._

Takes input as a command-line argument. Try it online!

Simple regex replacement, inserts a space before each match of [zfs]|one|[ent][iwh].


Jumping on the bandwagon of stealing borrowing Uriel's regex gives 23 bytes (with -s flag):

a@`..[eox]|[tse]?....`

Jelly, 44 bytes

Ṛ¹Ƥz⁶ZUwЀ“¢¤Ƙƒ⁺6j¹;Ċ-ḶṃżṃgɼṘƑUẏ{»Ḳ¤$€Ẏḟ1Ṭœṗ

Try it online!

Pyth, 35 27 23 bytes

Saved a lot of bytes by porting Uriel's approach.

:Q"..[eox]|[tse]?...."1

Try it here! Initial approach.

Retina, 20 bytes

!`..[eox]|[tse]?....

Try it online!

Python 2, 50 bytes

import re
re.compile('..[eox]|[tse]?....').findall

Try it online!

-3 thanks to Lynn.
-4 thanks to Uriel's answer's regex.

Retina, 28 bytes

t[ewh]|[zfs]|(ni|o)ne|ei
 $&

Try it online!