Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/cardinal_pythonlib/stringfunc.py : 43%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
2# cardinal_pythonlib/sqlalchemy/subproc.py
4"""
5===============================================================================
7 Original code copyright (C) 2009-2021 Rudolf Cardinal (rudolf@pobox.com).
9 This file is part of cardinal_pythonlib.
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
15 https://www.apache.org/licenses/LICENSE-2.0
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
23===============================================================================
24"""
26import re
27from typing import Any, Dict, Iterable, List
28import unicodedata
31# =============================================================================
32# Finding
33# =============================================================================
35def find_nth(s: str, x: str, n: int = 0, overlap: bool = False) -> int:
36 """
37 Finds the position of *n*\ th occurrence of ``x`` in ``s``, or ``-1`` if
38 there isn't one.
40 - The ``n`` parameter is zero-based (i.e. 0 for the first, 1 for the
41 second...).
42 - If ``overlap`` is true, allows fragments to overlap. If not, they must be
43 distinct.
45 As per
46 https://stackoverflow.com/questions/1883980/find-the-nth-occurrence-of-substring-in-a-string
47 """ # noqa
48 length_of_fragment = 1 if overlap else len(x)
49 i = -length_of_fragment
50 for _ in range(n + 1):
51 i = s.find(x, i + length_of_fragment)
52 if i < 0:
53 break
54 return i
57# =============================================================================
58# Splitting
59# =============================================================================
61def split_string(x: str, n: int) -> List[str]:
62 """
63 Split string into chunks of length n
64 """
65 # https://stackoverflow.com/questions/9475241/split-string-every-nth-character # noqa
66 return [x[i:i+n] for i in range(0, len(x), n)]
69# =============================================================================
70# Replacement
71# =============================================================================
73def multiple_replace(text: str, rep: Dict[str, str]) -> str:
74 """
75 Returns a version of ``text`` in which the keys of ``rep`` (a dict) have
76 been replaced by their values.
78 As per
79 https://stackoverflow.com/questions/6116978/python-replace-multiple-strings.
80 """
81 rep = dict((re.escape(k), v) for k, v in rep.items())
82 pattern = re.compile("|".join(rep.keys()))
83 return pattern.sub(lambda m: rep[re.escape(m.group(0))], text)
86def replace_in_list(stringlist: Iterable[str],
87 replacedict: Dict[str, str]) -> List[str]:
88 """
89 Returns a list produced by applying :func:`multiple_replace` to every
90 string in ``stringlist``.
92 Args:
93 stringlist: list of source strings
94 replacedict: dictionary mapping "original" to "replacement" strings
96 Returns:
97 list of final strings
99 """
100 newlist = []
101 for fromstring in stringlist:
102 newlist.append(multiple_replace(fromstring, replacedict))
103 return newlist
106# =============================================================================
107# Mangling to ASCII
108# =============================================================================
110def mangle_unicode_to_ascii(s: Any) -> str:
111 """
112 Mangle unicode to ASCII, losing accents etc. in the process.
113 """
114 # https://stackoverflow.com/questions/1207457
115 if s is None:
116 return ""
117 if not isinstance(s, str):
118 s = str(s)
119 return (
120 unicodedata.normalize('NFKD', s)
121 .encode('ascii', 'ignore') # gets rid of accents
122 .decode('ascii') # back to a string
123 )
126# =============================================================================
127# Making strings and string lists
128# =============================================================================
130def strnum(prefix: str, num: int, suffix: str = "") -> str:
131 """
132 Makes a string of the format ``<prefix><number><suffix>``.
133 """
134 return f"{prefix}{num}{suffix}"
137def strnumlist(prefix: str, numbers: List[int], suffix: str = "") -> List[str]:
138 """
139 Makes a string of the format ``<prefix><number><suffix>`` for every number
140 in ``numbers``, and returns them as a list.
141 """
142 return [f"{prefix}{num}{suffix}" for num in numbers]
145def strseq(prefix: str, first: int, last: int, suffix: str = "") -> List[str]:
146 """
147 Makes a string of the format ``<prefix><number><suffix>`` for every number
148 from ``first`` to ``last`` inclusive, and returns them as a list.
149 """
150 return [strnum(prefix, n, suffix) for n in range(first, last + 1)]