<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Regex on blog.iankulin.com</title><link>https://blog.iankulin.com/tags/regex/</link><description>Recent content in Regex on blog.iankulin.com</description><generator>Hugo</generator><language>en-AU</language><lastBuildDate>Wed, 08 Mar 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.iankulin.com/tags/regex/index.xml" rel="self" type="application/rss+xml"/><item><title>Recursive list of files in Linux</title><link>https://blog.iankulin.com/recursive-list-of-files-in-linux/</link><pubDate>Wed, 08 Mar 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/recursive-list-of-files-in-linux/</guid><description>&lt;p&gt;I&amp;rsquo;ve spent a few hours over the weekend migrating a media library from an external USB drive to the NAS, and in the process reorganised it, and in many cases bulk changed file names. I&amp;rsquo;ve also added a heap of metadata.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d like to check that I haven&amp;rsquo;t missed any files, but a side by side listing of each data source won&amp;rsquo;t do the trick, so I&amp;rsquo;ll probably end up pulling the data into a spreadsheet, but I&amp;rsquo;d like to get as close as possible with Linux-fu first.&lt;/p&gt;
&lt;p&gt;Before I go over my trial and error, and eventual solution, here&amp;rsquo;s how I&amp;rsquo;ve set up my test data for the examples. I thought I&amp;rsquo;d better start with something simple and small for testing commands.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-03-06-at-5.02.17-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-03-06-at-5.02.17-pm.png" width="495" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is actually the output of the &lt;code&gt;tree&lt;/code&gt; command on a &lt;code&gt;test&lt;/code&gt; directory I&amp;rsquo;ve created in my home directory. (I had to install it - &lt;code&gt;sudo apt install tree&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;What I need to end up with is something that recursively lists all the files, with one file per line, and it needs to include the directory tree to reach it. I should be able to pipe it through something to ignore lines that are just directories (and any other fluff).&lt;/p&gt;
&lt;h3 id="ls"&gt;ls&lt;/h3&gt;
&lt;p&gt;My go to for listing files is &lt;code&gt;ls -all&lt;/code&gt;, perhaps than can help us? It lists one line per file (along with permissions etc), so if we add &lt;code&gt;-R&lt;/code&gt; for recursive, that could be it. Here&amp;rsquo;s the output for &lt;code&gt;ls -all -R test&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total 16
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 4 ian ian 4096 Mar 6 16:36 .
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 4 ian ian 4096 Mar 6 16:36 ..
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 2 ian ian 4096 Mar 6 17:01 dir1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 2 ian ian 4096 Mar 6 17:01 dir2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir1:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total 8
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 2 ian ian 4096 Mar 6 17:01 .
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 4 ian ian 4096 Mar 6 16:36 ..
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 17:01 ignore.me
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 17:00 media1.ex1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 17:00 media1.ex2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 17:01 media3.ex1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 16:36 somefile
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 16:36 somefile2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir2:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total 8
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 2 ian ian 4096 Mar 6 17:01 .
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 4 ian ian 4096 Mar 6 16:36 ..
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 17:01 ignore.me
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 17:01 media4.ex1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 17:01 media5.ex1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 17:01 media6.ex2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 16:37 somefile
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 ian ian 0 Mar 6 16:37 somefile3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So we get one line per file, but the directory is on it&amp;rsquo;s own at the beginning of each directory listing.&lt;/p&gt;
&lt;h3 id="find"&gt;find&lt;/h3&gt;
&lt;p&gt;Based on &lt;a href="https://www.cyberciti.biz/faq/how-to-show-recursive-directory-listing-on-linux-or-unix/"&gt;this post&lt;/a&gt;, there is a command, &lt;code&gt;find&lt;/code&gt;, that might do what we want. The simple version would be &lt;code&gt;find test&lt;/code&gt; (remember test is the directory name).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ian@vm102-jellyfin:~$ find test
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir2/ignore.me
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir2/media4.ex1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir2/somefile
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir2/media5.ex1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir2/somefile3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir2/media6.ex2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir1/media3.ex1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir1/ignore.me
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir1/somefile
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir1/media1.ex1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir1/media1.ex2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test/dir1/somefile2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Well that is real close, but there&amp;rsquo;s no way to discern between a file and directory. In that same post, it;s suggested to use the &lt;code&gt;-ls&lt;/code&gt; option to see some more detail. Let&amp;rsquo;s try find &lt;code&gt;test -ls&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-03-06-at-5.20.59-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;This looks pretty close. If there was someway of using that &amp;rsquo;d&amp;rsquo; in the first position of the permissions output to eliminate those lines, we&amp;rsquo;d be well on our way. I have a feeling this is a &lt;code&gt;grep&lt;/code&gt; question. I have some basic grep, so for example I know I could pull all of those directories with &lt;code&gt;find test -ls | grep ' d'&lt;/code&gt;, or even invert it with the &lt;code&gt;-v&lt;/code&gt; flag to get just the files (which is out eventual goal).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-03-06-at-5.36.37-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;However, this is pretty hacky. A space followed by a lowercase could easily occur in a filename. What I really need to do is look at just that column which I think is character number 18. Off to &lt;a href="https://unix.stackexchange.com/questions/32170/find-all-lines-in-a-file-with-a-certain-character-at-a-certain-position"&gt;Stack Exchange&lt;/a&gt; I guess&amp;hellip;&lt;/p&gt;
&lt;h3 id="grep-with-regex"&gt;grep with regex&lt;/h3&gt;
&lt;p&gt;Okay, it turns out we can use regex with grep. I&amp;rsquo;m no expert in that either, but in regex the caret ^ represents the start of the line, a fullstop represents any character, and we can repeat that however many times we want by following it with a number in (escaped) curly braces. Something like &lt;code&gt;'^.{17}d'&lt;/code&gt; should do it.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-03-06-at-5.44.46-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Okay! We&amp;rsquo;re getting close. I also want to ignore all the metadata and just see the media files. This can be determined by the extensions - probably .avi .mp4 .mkv .mv4. With this test data, we&amp;rsquo;ll pretend it&amp;rsquo;s .ex1 and .ex2&lt;/p&gt;
&lt;h3 id="combining-grep-tests-with-logical-or"&gt;Combining grep tests with logical or&lt;/h3&gt;
&lt;p&gt;I guess I could build some sort of super regex combined with the first one, but I&amp;rsquo;m only dealing with thousands of files, not millions so the extra overhead of piping through another grep is not going to be a drama, and I can simplify my work. In the same way that the caret ^ marks the start of a line, the dollar $ marks the end of it. So to just get the .ex1 files something like &lt;code&gt;'\.ex1$'&lt;/code&gt; should do it. The backslash at the start is to escape the period, because here we want that to mean a literal full stop, and not a wildcard for any character.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-03-06-at-5.55.25-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Nice, but remember I&amp;rsquo;ve got a big list of extensions, so I need to logical or a few together. This is done by putting the expressions with a pipe between them. I had a couple of goes at this with no luck and that familiar feeling of being out of my depth with regex. However, there&amp;rsquo;s a grep way out of this, because the grep flag -e allows us to OR matching expressions.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-03-06-at-6.06.52-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re definitely getting somewhere. You might think at this point that the chance of a directory name ending in .mp4 or one of the other media extensions is no low we could ignore it, and you&amp;rsquo;d probably be right. But as a matter of programmer pride, I never like to leave a future problem, so I&amp;rsquo;ll be keeping the directory rejecting grep. So now my command looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;find test -ls | grep -v &amp;#39;^.{17}d&amp;#39; | grep -e &amp;#39;\.ex1$&amp;#39; -e &amp;#39;\.ex2$&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Any experienced regex people would be pointing out the match for .ex1 .ex2 can easily be merged into a simple expression, but remember when I do this for real I&amp;rsquo;ve got a list of more complex extensions to test for.&lt;/p&gt;
&lt;h3 id="cut"&gt;cut&lt;/h3&gt;
&lt;p&gt;All that text at the beginning of these lines is not needed. Surely I can trim that off somehow? Yep - there&amp;rsquo;s a command &lt;code&gt;cut&lt;/code&gt; that does exactly that. The -b flag specifies which byte to extract, and this can also be a range. putting a dash after the position number says to output all of the bytes after that position. So if we applied &lt;code&gt;cut -b 5-&lt;/code&gt; to the string &lt;code&gt;123456789&lt;/code&gt;, the output would be &lt;code&gt;56789&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-03-06-at-6.26.18-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Bingo. Just one more problem. In my data, I have a heap of files with a valid extension but I want to exclude them based on their file name. Every directory with a movie has a trailer named &lt;code&gt;trailer.mp4&lt;/code&gt;, so I need to eliminate them. To simulate this, lets add in another extension with our test data.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-03-06-at-6.29.48-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;So I want to take out the lines that include &amp;lsquo;/ignore.me&amp;rsquo;. I should be able to do this with another &lt;code&gt;grep -v&lt;/code&gt; regex on a line end. Something like &lt;code&gt;grep -v 'ignore.me$'&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-03-06-at-6.33.22-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;And we&amp;rsquo;re done! I&amp;rsquo;ll just direct this into a file, run it on both disks and pull them into Excel to separate the file names and directories, and sort them to compare.&lt;/p&gt;</description></item><item><title>Regex to split a string with two different characters</title><link>https://blog.iankulin.com/regex-to-split-a-string-with-two-different-characters/</link><pubDate>Wed, 30 Nov 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/regex-to-split-a-string-with-two-different-characters/</guid><description>&lt;p&gt;I&amp;rsquo;m working on the behaviour tickets app, and wanted a visually functional version to share with stakeholders this week to get some feedback. As usual in this situation, I&amp;rsquo;m pressed for time so feeling the pressure to take some liberties with code quality that I&amp;rsquo;ll come back and fix one day.&lt;/p&gt;
&lt;p&gt;In a salient lesson of why that&amp;rsquo;s usually a bad idea, I&amp;rsquo;ve ended up googling to try and understand regex instead of writing code.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the problem I was trying to quickly solve. I&amp;rsquo;ve used a string for what should probably have been a struct. It looks like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;let myString = &amp;quot;Some behaviour (expectation)&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Its super easy to combine values together into a string, but substantially more difficult (and dangerous) to extract them out again. I want to get &lt;code&gt;&amp;quot;Some behaviour&amp;quot;&lt;/code&gt; and &lt;code&gt;&amp;quot;expectation&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is a String.split() function that takes a separator and returns an array of the strings split on that. That could work in this case, but I&amp;rsquo;d have to split a couple of times since I&amp;rsquo;m using two different separators. Swift 5.7 released in 2022 introduced a regex (regular expression) type, and this can be used as the argument for the split() method. Sounds perfect. Here&amp;rsquo;s the code that I ended up with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let myString = &amp;#34;Some behaviour (expectation)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let regex = /\ \(|\)/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let splits = myString.split(separator: regex)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(splits)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;// [&amp;#34;Some behaviour&amp;#34;, &amp;#34;expectation&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I think it&amp;rsquo;s fair to say that regex is powerful, but not intuitive. There are many &lt;a href="https://regex101.com/"&gt;online tools&lt;/a&gt; to help with this. But let me step through building this expression to give you an idea of what&amp;rsquo;s going on.&lt;/p&gt;
&lt;p&gt;The first thing is that the expression is enclosed in two forward slashes. So if we just wanted to split on lower case &amp;lsquo;o&amp;rsquo; the expression would be &lt;code&gt;/o/&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let myString = &amp;#34;Some behaviour (expectation)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let regex = /o/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let splits = myString.split(separator: regex)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(splits)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;//[&amp;#34;S&amp;#34;, &amp;#34;me behavi&amp;#34;, &amp;#34;ur (expectati&amp;#34;, &amp;#34;n)&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But we want to split on an opening bracket. You might think &lt;code&gt;/)/&lt;/code&gt; would work, but brackets are part of the regex syntax, so they have to be escaped. This is done with a back slash.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let myString = &amp;#34;Some behaviour (expectation)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let regex = /\(/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let splits = myString.split(separator: regex)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(splits)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&amp;#34;Some behaviour &amp;#34;, &amp;#34;expectation)&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I don&amp;rsquo;t want that space at the end of &amp;ldquo;Some behaviour &amp;quot; so I&amp;rsquo;ll add that to the regex. Spaces are not allowed at the start of a regex, so that needs to be escaped too.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let myString = &amp;#34;Some behaviour (expectation)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let regex = /\ \(/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let splits = myString.split(separator: regex)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(splits)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;// [&amp;#34;Some behaviour&amp;#34;, &amp;#34;expectation)&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To match the end bracket, we&amp;rsquo;ll need to add an OR to our expression. In regex, this is a | (pipe), and of course we need to escape the bracket again.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let myString = &amp;#34;Some behaviour (expectation)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let regex = /\ \(|\)/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let splits = myString.split(separator: regex)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(splits)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&amp;#34;Some behaviour&amp;#34;, &amp;#34;expectation&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I played with this in an Xcode playground. I normally don&amp;rsquo;t use them because I love the iCloud sync that I get with the Playgrounds app to my iPad, but it seems like the app version is not on Swift 5.7 yet.&lt;/p&gt;</description></item></channel></rss>