diff --git a/trafaret/__init__.py b/trafaret/__init__.py
index 13a10af..43eb57c 100644
--- a/trafaret/__init__.py
+++ b/trafaret/__init__.py
@@ -51,10 +51,12 @@ from .numeric import (
     ToInt,
     ToDecimal,
 )
-from .regexp import Regexp, RegexpRaw
+from .regexp import Regexp, RegexpRaw, RegexpString
 from .internet import (
     URL,
+    URLSafe,
     Email,
+    Hex,
     IPv4,
     IPv6,
     IP,
@@ -104,8 +106,11 @@ __all__ = (
 
     "Regexp",
     "RegexpRaw",
+    "RegexpString",
     "URL",
+    "URLSafe",
     "Email",
+    "Hex",
     "IPv4",
     "IPv6",
     "IP",
diff --git a/trafaret/internet.py b/trafaret/internet.py
index 273bd9a..5ae73d8 100644
--- a/trafaret/internet.py
+++ b/trafaret/internet.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 
 import re
-from .regexp import Regexp
+from .regexp import Regexp, RegexpString
 from .base import String, FromBytes, OnError, WithRepr
 from .lib import py3
 from . import codes
@@ -41,7 +41,7 @@ to_str = OnError(FromBytes('utf-8') | String(), 'value is not a string', code=co
 
 email_regexp_trafaret = OnError(to_str & Regexp(EMAIL_REGEXP), 'value is not a valid email address')
 email_trafaret = (email_regexp_trafaret | (to_str & email_idna_encode & email_regexp_trafaret))
-Email = to_str & String(allow_blank=True) & OnError(
+Email = to_str & OnError(
     String(max_length=MAX_EMAIL_LEN) & email_trafaret,
     'value is not a valid email address',
     code=codes.IS_NOT_VALID_EMAIL,
@@ -49,6 +49,21 @@ Email = to_str & String(allow_blank=True) & OnError(
 Email = WithRepr(Email, '<Email>')
 
 
+class Hex(RegexpString):
+    regex = r'^[0-9a-f]*$'
+    str_method = "lower"
+
+    def __repr__(self):
+        return '<Hex>'
+
+
+class URLSafe(RegexpString):
+    regex = r'^[0-9A-Za-z-_]*$'
+
+    def __repr__(self):
+        return '<URLSafe>'
+
+
 URL_REGEXP = re.compile(
     r'^(?:http|ftp)s?://'  # http:// or https://
     r'(?:\S+(?::\S*)?@)?'  # user and password
diff --git a/trafaret/regexp.py b/trafaret/regexp.py
index 653c39b..26c7e91 100644
--- a/trafaret/regexp.py
+++ b/trafaret/regexp.py
@@ -1,5 +1,5 @@
 import re
-from .base import Trafaret
+from .base import Trafaret, String
 from .lib import STR_TYPES
 from . import codes
 
@@ -29,3 +29,22 @@ class RegexpRaw(Trafaret):
 class Regexp(RegexpRaw):
     def check_and_return(self, value):
         return super(Regexp, self).check_and_return(value).group()
+
+
+class RegexpString(String, Regexp):
+    __slots__ = ()
+    # regex: str
+    str_method = None
+
+    def __init__(self, *args, **kwargs):
+        String.__init__(self, *args, **kwargs)
+        Regexp.__init__(self, self.regex)
+
+    def check_and_return(self, value):
+        str_value = String.check_and_return(self, value)
+        if self.str_method is not None:
+            str_value = getattr(str_value, self.str_method)()
+        return Regexp.check_and_return(self, str_value)
+
+    def __repr__(self):
+        return '<RegexpString "%s">' % self.regex

Hex and URLSafe validators
Would be nice to have a couple more String validators that validated the string as e.g. hex or url safe. Matching for example, the output you'd get from `secrets.token_hex()` and `secrets.token_urlsafe()`.
diff --git a/tests/test_base.py b/tests/test_base.py
index 8ca7cae..0ca167b 100644
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -761,6 +761,25 @@ class TestRegexpTrafaret:
         assert repr(t.RegexpRaw('.*(cat).*')) == '<Regexp ".*(cat).*">'
 
 
+class TestRegexpString:
+    def test_regexpstring(self):
+        class R(t.RegexpString):
+            regex = r'^A?$'
+            str_method = 'upper'
+
+        assert R().check('a') == 'A'
+
+        res = extract_error(R(), 'b')
+        assert res == 'does not match pattern ^A?$'
+
+        res = extract_error(R(), '')
+        assert res == 'blank value is not allowed'
+
+        assert R(allow_blank=True).check('') == ''
+
+        assert repr(R()) == '<RegexpString "^A?$">'
+
+
 class TestTrafaretMeta:
     def test_meta(self):
         res = (t.ToInt() >> (lambda x: x * 2) >> (lambda x: x * 3)).check(1)
diff --git a/tests/test_internet.py b/tests/test_internet.py
index 1c6147d..de2d808 100644
--- a/tests/test_internet.py
+++ b/tests/test_internet.py
@@ -100,6 +100,58 @@ class TestEmailTrafaret:
             t.Email.check(b'ahha@\xd0\xbf\xd1\x80\xd0\xb8\xd0\xbc\xd0\xb5\xd1\x80.\xd1\xd1\x84')
 
 
+class TestHexTrafaret:
+    def test_hex(self):
+        res = t.Hex().check('bbd3ec7776d5684d87')
+        assert res == 'bbd3ec7776d5684d87'
+        res = t.Hex().check('47CCB8AEEC972')
+        assert res == '47ccb8aeec972'
+        res = extract_error(t.Hex(), 'apple')
+        assert res == 'does not match pattern ^[0-9a-f]*$'
+        res = extract_error(t.Hex(), '')
+        assert res == 'blank value is not allowed'
+        res = t.Hex(allow_blank=True).check('')
+        assert res == ''
+        res = t.Hex(min_length=2, max_length=3).check('12a')
+        assert res == '12a'
+        res = extract_error(t.Hex(min_length=2, max_length=6), '1')
+        assert res == 'String is shorter than 2 characters'
+        res = extract_error(t.Hex(min_length=2, max_length=6), '1234567')
+        assert res == 'String is longer than 6 characters'
+
+        res = t.Hex(min_length=0, max_length=6, allow_blank=True).check('abc')
+        assert res == 'abc'
+
+    def test_repr(self):
+        res = t.Hex()
+        assert repr(res) == '<Hex>'
+
+
+class TestURLSafeTrafaret:
+    def test_urlsafe(self):
+        res = t.URLSafe().check('sB3ny_Vmu-C')
+        assert res == 'sB3ny_Vmu-C'
+        res = extract_error(t.URLSafe(), 'app/le')
+        assert res == 'does not match pattern ^[0-9A-Za-z-_]*$'
+        res = extract_error(t.URLSafe(), '')
+        assert res == 'blank value is not allowed'
+        res = t.URLSafe(allow_blank=True).check('')
+        assert res == ''
+        res = t.URLSafe(min_length=2, max_length=3).check('1-z')
+        assert res == '1-z'
+        res = extract_error(t.URLSafe(min_length=2, max_length=6), '_')
+        assert res == 'String is shorter than 2 characters'
+        res = extract_error(t.URLSafe(min_length=2, max_length=6), '1234567')
+        assert res == 'String is longer than 6 characters'
+
+        res = t.URLSafe(min_length=0, max_length=6, allow_blank=True).check('a_c')
+        assert res == 'a_c'
+
+    def test_repr(self):
+        res = t.URLSafe()
+        assert repr(res) == '<URLSafe>'
+
+
 def test_ipv4(valid_ips_v4, invalid_ips_v4):
     ip = t.IPv4
 

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-asyncio flake8 pylint pymongo python-dateutil
apt-get update apt-get install -y gcc
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
astroid==2.11.7
attrs==22.2.0
certifi==2021.5.30
coverage==6.2
dill==0.3.4
flake8==5.0.4
importlib-metadata==4.2.0
iniconfig==1.1.1
isort==5.10.1
lazy-object-proxy==1.7.1
mccabe==0.7.0
packaging==21.3
platformdirs==2.4.0
pluggy==1.0.0
py==1.11.0
pycodestyle==2.9.1
pyflakes==2.5.0
pylint==2.13.9
pymongo==4.1.1
pyparsing==3.1.4
pytest==7.0.1
pytest-asyncio==0.16.0
pytest-cov==4.0.0
python-dateutil==2.9.0.post0
six==1.17.0
tomli==1.2.3
-e git+https://github.com/Deepwalker/trafaret.git@2e62930aef61316268dc3abbe4b30cf90d27a320#egg=trafaret
typed-ast==1.5.5
typing_extensions==4.1.1
wrapt==1.16.0
zipp==3.6.0

name: trafaret
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - astroid==2.11.7
      - attrs==22.2.0
      - coverage==6.2
      - dill==0.3.4
      - flake8==5.0.4
      - importlib-metadata==4.2.0
      - iniconfig==1.1.1
      - isort==5.10.1
      - lazy-object-proxy==1.7.1
      - mccabe==0.7.0
      - packaging==21.3
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - py==1.11.0
      - pycodestyle==2.9.1
      - pyflakes==2.5.0
      - pylint==2.13.9
      - pymongo==4.1.1
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-asyncio==0.16.0
      - pytest-cov==4.0.0
      - python-dateutil==2.9.0.post0
      - six==1.17.0
      - tomli==1.2.3
      - typed-ast==1.5.5
      - typing-extensions==4.1.1
      - wrapt==1.16.0
      - zipp==3.6.0
prefix: /opt/conda/envs/trafaret

tests/test_base.py::TestRegexpString::test_regexpstring tests/test_internet.py::TestHexTrafaret::test_hex tests/test_internet.py::TestHexTrafaret::test_repr tests/test_internet.py::TestURLSafeTrafaret::test_urlsafe tests/test_internet.py::TestURLSafeTrafaret::test_repr
tests/test_base.py::TestTrafaret::test_any tests/test_base.py::TestTrafaret::test_ensure tests/test_base.py::TestTrafaret::test_is_valid tests/test_base.py::TestAnyTrafaret::test_any tests/test_base.py::TestAnyTrafaret::test_repr tests/test_base.py::TestAtomTrafaret::test_atom tests/test_base.py::TestBoolTrafaret::test_bool[True-True] tests/test_base.py::TestBoolTrafaret::test_bool[False-False] tests/test_base.py::TestBoolTrafaret::test_extract_error tests/test_base.py::TestBoolTrafaret::test_repr tests/test_base.py::TestCallTrafaret::test_call tests/test_base.py::TestCallTrafaret::test_repr tests/test_base.py::TestCallTrafaret::test_should_be_callable tests/test_base.py::TestCallableTrafaret::test_callable tests/test_base.py::TestCallableTrafaret::test_repr tests/test_base.py::TestBasics::test_callable tests/test_base.py::TestBasics::test_auto_call tests/test_base.py::TestBasics::test_class tests/test_base.py::TestBasics::test_upper tests/test_base.py::TestDictTrafaret::test_base tests/test_base.py::TestDictTrafaret::test_repr tests/test_base.py::TestDictTrafaret::test_key_shadowed tests/test_base.py::TestDictTrafaret::test_kwargs_extra tests/test_base.py::TestDictTrafaret::test_kwargs_ignore tests/test_base.py::TestDictTrafaret::test_add_kwargs_ignore tests/test_base.py::TestDictTrafaret::test_add_kwargs_ignore_any tests/test_base.py::TestDictTrafaret::test_add_kwargs_extra tests/test_base.py::TestDictTrafaret::test_callable_key tests/test_base.py::TestDictTrafaret::test_base2 tests/test_base.py::TestDictTrafaret::test_base3 tests/test_base.py::TestDictTrafaret::test_add tests/test_base.py::TestDictTrafaret::test_bad_add_names tests/test_base.py::TestDictTrafaret::test_bad_add_to_names tests/test_base.py::TestDictTrafaret::test_add_to_names_list_of_keys tests/test_base.py::TestDictTrafaret::test_add_to_names_dict_of_keys tests/test_base.py::TestDictTrafaret::test_bad_args_add tests/test_base.py::TestDictTrafaret::test_mapping_interface tests/test_base.py::TestDictTrafaret::test_keys_must_be_callable tests/test_base.py::TestDictTrafaret::test_clone tests/test_base.py::TestDictKeys::test_dict_keys tests/test_base.py::TestEnumTrafaret::test_enum tests/test_base.py::TestEnumTrafaret::test_repr tests/test_base.py::TestToFloat::test_float tests/test_base.py::TestToFloat::test_float_repr tests/test_base.py::TestToFloat::test_float_meta_repr tests/test_base.py::TestForwardTrafaret::test_forward tests/test_base.py::TestForwardTrafaret::test_repr tests/test_base.py::TestToIntTrafaret::test_int tests/test_base.py::TestToIntTrafaret::test_repr tests/test_base.py::TestList::test_list tests/test_base.py::TestList::test_list_meta tests/test_base.py::TestList::test_2_0_regression tests/test_base.py::TestList::test_list_repr tests/test_base.py::TestIterableTrafaret::test_iterable tests/test_base.py::TestIterableTrafaret::test_repr tests/test_base.py::TestMappingTrafaret::test_mapping tests/test_base.py::TestMappingTrafaret::test_repr tests/test_base.py::TestNullTrafaret::test_null tests/test_base.py::TestNullTrafaret::test_repr tests/test_base.py::TestOrNotToTest::test_or tests/test_base.py::TestOrNotToTest::test_operator tests/test_base.py::TestOrNotToTest::test_repr tests/test_base.py::TestAndTest::test_and tests/test_base.py::TestAndTest::test_raise_error tests/test_base.py::TestAndTest::test_repr tests/test_base.py::TestToBoolTrafaret::test_str_bool[t-True] tests/test_base.py::TestToBoolTrafaret::test_str_bool[true-True] tests/test_base.py::TestToBoolTrafaret::test_str_bool[y-True] tests/test_base.py::TestToBoolTrafaret::test_str_bool[yes-True] tests/test_base.py::TestToBoolTrafaret::test_str_bool[On-True] tests/test_base.py::TestToBoolTrafaret::test_str_bool[1-True0] tests/test_base.py::TestToBoolTrafaret::test_str_bool[1-True1] tests/test_base.py::TestToBoolTrafaret::test_str_bool[1.0-True] tests/test_base.py::TestToBoolTrafaret::test_str_bool[True-True] tests/test_base.py::TestToBoolTrafaret::test_str_bool[false-False] tests/test_base.py::TestToBoolTrafaret::test_str_bool[n-False] tests/test_base.py::TestToBoolTrafaret::test_str_bool[no-False] tests/test_base.py::TestToBoolTrafaret::test_str_bool[off-False] tests/test_base.py::TestToBoolTrafaret::test_str_bool[0-False0] tests/test_base.py::TestToBoolTrafaret::test_str_bool[0-False1] tests/test_base.py::TestToBoolTrafaret::test_str_bool[0.0-False] tests/test_base.py::TestToBoolTrafaret::test_str_bool[False-False] tests/test_base.py::TestToBoolTrafaret::test_extract_error tests/test_base.py::TestToBoolTrafaret::test_repr tests/test_base.py::TestStringTrafaret::test_string tests/test_base.py::TestStringTrafaret::test_repr tests/test_base.py::TestDateTrafaret::test_date tests/test_base.py::TestDateTrafaret::test_to_date tests/test_base.py::TestDateTrafaret::test_repr tests/test_base.py::TestDateTimeTrafaret::test_datetime tests/test_base.py::TestDateTimeTrafaret::test_to_datetime tests/test_base.py::TestDateTimeTrafaret::test_repr tests/test_base.py::TestToBytesTrafaret::test_bytes tests/test_base.py::TestToBytesTrafaret::test_repr tests/test_base.py::TestFromBytesTrafaret::test_bytes tests/test_base.py::TestFromBytesTrafaret::test_repr tests/test_base.py::TestRegexpTrafaret::test_regexp tests/test_base.py::TestRegexpTrafaret::test_regexp_raw tests/test_base.py::TestRegexpTrafaret::test_regexp_raw_error tests/test_base.py::TestRegexpTrafaret::test_repr tests/test_base.py::TestTrafaretMeta::test_meta tests/test_base.py::TestTrafaretMeta::test_repr tests/test_base.py::TestTupleTrafaret::test_tuple tests/test_base.py::TestTupleTrafaret::test_repr tests/test_base.py::TestTypeTrafaret::test_type tests/test_base.py::TestTypeTrafaret::test_repr tests/test_base.py::TestSubclassTrafaret::test_subclass tests/test_base.py::TestSubclassTrafaret::test_repr tests/test_base.py::TestOnErrorTrafaret::test_on_error tests/test_base.py::TestOnErrorTrafaret::test_on_error_ensured_trafaret tests/test_base.py::TestOnErrorTrafaret::test_on_error_data_error tests/test_base.py::test_with_repr tests/test_base.py::TestGuard::test_keywords_only tests/test_base.py::TestGuard::test_class_method tests/test_base.py::TestGuard::test_args_checks tests/test_base.py::test_ignore tests/test_base.py::test_deprecated tests/test_internet.py::TestURLTrafaret::test_url tests/test_internet.py::TestURLTrafaret::test_bad_str tests/test_internet.py::TestEmailTrafaret::test_email tests/test_internet.py::TestEmailTrafaret::test_bad_str tests/test_internet.py::test_ipv4 tests/test_internet.py::test_ipv6
trafaret/regexp.py trafaret/internet.py trafaret/__init__.py
trafaret/regexp.py trafaret/internet.py trafaret/__init__.py