diff --git a/README.md b/README.md
index cfa9e9fb3..5f6ce7854 100644
--- a/README.md
+++ b/README.md
@@ -237,24 +237,20 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
                                      compare against a number, key = 'LITERAL'
                                      (like "uploader = 'Mike Smith'", also works
                                      with !=) to match against a string literal
-                                     and & to require multiple matches. Values
-                                     which are not known are excluded unless you
-                                     put a question mark (?) after the operator.
-                                     For example, to only match videos that have
-                                     been liked more than 100 times and disliked
-                                     less than 50 times (or the dislike
-                                     functionality is not available at the given
-                                     service), but who also have a description,
-                                     use --match-filter "like_count > 100 &
+                                     and & to require multiple matches. The
+                                     string comparisons ^= (starts with), *=
+                                     (contains) and $= (ends with). Every
+                                     comparison operator can be prefixed by ! to
+                                     negate it. Values which are not known are
+                                     excluded unless you put a question mark (?)
+                                     after the operator. For example, to only
+                                     match videos that have been liked more than
+                                     100 times and disliked less than 50 times
+                                     (or the dislike functionality is not
+                                     available at the given service), but who
+                                     also have a description, use
+                                     --match-filter "like_count > 100 &
                                      dislike_count <? 50 & description" .
-                                     For matching strings, the oparators ~= and
-                                     !~= check for string containment and
-                                     exclusion. The operators *= and !*= search
-                                     for a regular expression.
-                                     For example, to only match videos which
-                                     have neither 'sponsored' nor 'Sponsored' in
-                                     the title, use --match-filter "title !*=
-                                     '[Ss]ponsored'"
     --no-playlist                    Download only the video, if the URL refers
                                      to a video and a playlist.
     --yes-playlist                   Download the playlist, if the URL refers to
diff --git a/test/test_utils.py b/test/test_utils.py
index 259c4763e..ddfdfcaaf 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -1178,7 +1178,6 @@ ffmpeg version 2.4.4 Copyright (c) 2000-2014 the FFmpeg ...'''), '2.4.4')
             '9999 51')
 
     def test_match_str(self):
-        self.assertRaises(ValueError, match_str, 'xy>foobar', {})
         self.assertFalse(match_str('xy', {'x': 1200}))
         self.assertTrue(match_str('!xy', {'x': 1200}))
         self.assertTrue(match_str('x', {'x': 1200}))
@@ -1195,6 +1194,17 @@ ffmpeg version 2.4.4 Copyright (c) 2000-2014 the FFmpeg ...'''), '2.4.4')
         self.assertTrue(match_str('y=foobar42', {'y': 'foobar42'}))
         self.assertFalse(match_str('y!=foobar42', {'y': 'foobar42'}))
         self.assertTrue(match_str('y!=foobar2', {'y': 'foobar42'}))
+        self.assertTrue(match_str('y^=foo', {'y': 'foobar42'}))
+        self.assertFalse(match_str('y!^=foo', {'y': 'foobar42'}))
+        self.assertFalse(match_str('y^=bar', {'y': 'foobar42'}))
+        self.assertTrue(match_str('y!^=bar', {'y': 'foobar42'}))
+        self.assertRaises(ValueError, match_str, 'x^=42', {'x': 42})
+        self.assertTrue(match_str('y*=bar', {'y': 'foobar42'}))
+        self.assertFalse(match_str('y!*=bar', {'y': 'foobar42'}))
+        self.assertFalse(match_str('y*=baz', {'y': 'foobar42'}))
+        self.assertTrue(match_str('y!*=baz', {'y': 'foobar42'}))
+        self.assertTrue(match_str('y$=42', {'y': 'foobar42'}))
+        self.assertFalse(match_str('y$=43', {'y': 'foobar42'}))
         self.assertFalse(match_str(
             'like_count > 100 & dislike_count <? 50 & description',
             {'like_count': 90, 'description': 'foo'}))
diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py
index a774b0c9d..443e29baa 100644
--- a/youtube_dl/utils.py
+++ b/youtube_dl/utils.py
@@ -4368,15 +4368,13 @@ def _match_one(filter_part, dct):
         '>': operator.gt,
         '>=': operator.ge,
         '=': operator.eq,
-        '!=': operator.ne,
-        '~=': operator.contains,
-        '!~=': lambda left, right: not operator.contains(left, right),
-        '*=': lambda left, right: bool(re.search(right, left)),
-        '!*=': lambda left, right: not bool(re.search(right, left)),
+        '*=': operator.contains,
+        '^=': lambda attr, value: attr.startswith(value),
+        '$=': lambda attr, value: attr.endswith(value),
     }
     operator_rex = re.compile(r'''(?x)\s*
         (?P<key>[a-z_]+)
-        \s*(?P<op>%s)(?P<none_inclusive>\s*\?)?\s*
+        \s*(?P<negation>!\s*)?(?P<op>%s)(?P<none_inclusive>\s*\?)?\s*
         (?:
             (?P<intval>[0-9.]+(?:[kKmMgGtTpPeEzZyY]i?[Bb]?)?)|
             (?P<quote>["\'])(?P<quotedstrval>(?:\\.|(?!(?P=quote)|\\).)+?)(?P=quote)|
@@ -4386,7 +4384,11 @@ def _match_one(filter_part, dct):
         ''' % '|'.join(map(re.escape, COMPARISON_OPERATORS.keys())))
     m = operator_rex.search(filter_part)
     if m:
-        op = COMPARISON_OPERATORS[m.group('op')]
+        unnegated_op = COMPARISON_OPERATORS[m.group('op')]
+        if m.group('negation'):
+            op = lambda attr, value: not unnegated_op(attr, value)
+        else:
+            op = unnegated_op
         actual_value = dct.get(m.group('key'))
         if (m.group('quotedstrval') is not None
             or m.group('strval') is not None
@@ -4401,7 +4403,7 @@ def _match_one(filter_part, dct):
             if quote is not None:
                 comparison_value = comparison_value.replace(r'\%s' % quote, quote)
         else:
-            if m.group('op') in ('~=', '!~=', '*=', '!*='):
+            if m.group('op') in ('*=', '^=', '$='):
                 raise ValueError(
                     'Operator %s only supports string values!' % m.group('op'))
             try: