Mercurial > octave
comparison scripts/strings/endsWith.m @ 28232:49384057fb03
endsWith.m: overhaul function.
* endsWith.m: Use @group within @example blocks in documentation. Clarify
docstring text. Add regexp, strncmp, strncmpi to @seealso reference.
Combine input validation in to smaller code blocks. Eliminate try/catch
block by using isreal() test. Use cellfun() rather than for loop with flip()
to reverse STR and PATTERN. Declare retval to be of logical type rather
than double type. Update BIST tests.
author | Rik <rik@octave.org> |
---|---|
date | Mon, 20 Apr 2020 08:28:22 -0700 |
parents | d052c11d47c6 |
children | a6edb8097ec6 |
comparison
equal
deleted
inserted
replaced
28231:cbcf6c064622 | 28232:49384057fb03 |
---|---|
24 ######################################################################## | 24 ######################################################################## |
25 | 25 |
26 ## -*- texinfo -*- | 26 ## -*- texinfo -*- |
27 ## @deftypefn {} {@var{retval} =} endsWith (@var{str}, @var{pattern}) | 27 ## @deftypefn {} {@var{retval} =} endsWith (@var{str}, @var{pattern}) |
28 ## @deftypefnx {} {@var{retval} =} endsWith (@var{str}, @var{pattern}, "IgnoreCase", @var{confirm_ignore}) | 28 ## @deftypefnx {} {@var{retval} =} endsWith (@var{str}, @var{pattern}, "IgnoreCase", @var{confirm_ignore}) |
29 ## Checks that a cell array of strings ends with a pattern | 29 ## @deftypefnx {} {@var{retval} =} endsWith (@var{str}, @var{pattern}, "IgnoreCase", @var{ignore_case}) |
30 ## Check whether string(s) end with pattern(s). | |
30 ## | 31 ## |
31 ## Return an array of logical values that indicates which strings in the cell | 32 ## Return an array of logical values that indicates which string(s) in the |
32 ## array of strings --- or the string --- @var{str} ends with a string in the | 33 ## input @var{str} (a single string or cell array of strings) end with |
33 ## cell array of strings --- or the string --- @var{pattern}. | 34 ## the input @var{pattern} (a single string or cell array of strings). |
34 ## | 35 ## |
35 ## If the parameter @qcode{"IgnoreCase"} is set to true, then the function | 36 ## If the value of the parameter @qcode{"IgnoreCase"} is true, then the |
36 ## will ignore the letter case of @var{str} and @var{pattern}. By default, the | 37 ## function will ignore the letter case of @var{str} and @var{pattern}. By |
37 ## comparison is case sensitive. | 38 ## default, the comparison is case sensitive. |
38 ## | 39 ## |
39 ## Examples: | 40 ## Examples: |
40 ## | 41 ## |
41 ## @example | 42 ## @example |
43 ## @group | |
42 ## ## one string and one pattern while considering case | 44 ## ## one string and one pattern while considering case |
43 ## endsWith ("hello", "lo") | 45 ## endsWith ("hello", "lo") |
44 ## @result{} 1 | 46 ## @result{} 1 |
47 ## @end group | |
45 ## | 48 ## |
49 ## @group | |
46 ## ## one string and one pattern while ignoring case | 50 ## ## one string and one pattern while ignoring case |
47 ## endsWith ("hello", "LO", "IgnoreCase", true) | 51 ## endsWith ("hello", "LO", "IgnoreCase", true) |
48 ## @result{} 1 | 52 ## @result{} 1 |
53 ## @end group | |
49 ## | 54 ## |
55 ## @group | |
50 ## ## multiple strings and multiple patterns while considering case | 56 ## ## multiple strings and multiple patterns while considering case |
51 ## endsWith (@{"tests.txt", "mydoc.odt", "myFunc.m", "results.pptx"@}, | 57 ## endsWith (@{"tests.txt", "mydoc.odt", "myFunc.m", "results.pptx"@}, |
52 ## @{".docx", ".odt", ".txt"@}) | 58 ## @{".docx", ".odt", ".txt"@}) |
53 ## @result{} 1 1 0 0 | 59 ## @result{} 1 1 0 0 |
60 ## @end group | |
54 ## | 61 ## |
62 ## @group | |
55 ## ## multiple strings and one pattern while considering case | 63 ## ## multiple strings and one pattern while considering case |
56 ## endsWith (@{"TESTS.TXT", "mydoc.odt", "result.txt", "myFunc.m"@}, | 64 ## endsWith (@{"TESTS.TXT", "mydoc.odt", "result.txt", "myFunc.m"@}, |
57 ## ".txt", "IgnoreCase", false) | 65 ## ".txt", "IgnoreCase", false) |
58 ## @result{} 0 0 1 0 | 66 ## @result{} 0 0 1 0 |
67 ## @end group | |
59 ## | 68 ## |
69 ## @group | |
60 ## ## multiple strings and one pattern while ignoring case | 70 ## ## multiple strings and one pattern while ignoring case |
61 ## endsWith (@{"TESTS.TXT", "mydoc.odt", "result.txt", "myFunc.m"@}, | 71 ## endsWith (@{"TESTS.TXT", "mydoc.odt", "result.txt", "myFunc.m"@}, |
62 ## ".txt", "IgnoreCase", true) | 72 ## ".txt", "IgnoreCase", true) |
63 ## @result{} 1 0 1 0 | 73 ## @result{} 1 0 1 0 |
74 ## @end group | |
64 ## @end example | 75 ## @end example |
65 ## | 76 ## |
66 ## @seealso{startsWith, strcmp, strcmpi} | 77 ## @seealso{startsWith, regexp, strncmp, strncmpi} |
67 ## @end deftypefn | 78 ## @end deftypefn |
68 | 79 |
69 function retval = endsWith (str, pattern, ignore_case, confirm_ignore) | 80 function retval = endsWith (str, pattern, IgnoreCase, ignore_case) |
70 if (! (nargin == 2 || nargin == 4)) | 81 |
82 if (nargin != 2 && nargin != 4) | |
71 print_usage (); | 83 print_usage (); |
72 endif | 84 endif |
73 | 85 |
74 ## check input str and pattern | 86 ## Validate input str and pattern |
75 if (! (iscellstr (str) || ischar (str)) | 87 if (! (iscellstr (str) || ischar (str))) |
76 || ! (iscellstr (pattern) || ischar (pattern))) | 88 error ("endsWith: STR must be a string or cell array of strings"); |
77 error ("endsWith: arguments must be strings or cell arrays of strings"); | 89 endif |
78 else | 90 if (! (iscellstr (pattern) || ischar (pattern))) |
79 str = cellstr (str); | 91 error ("endsWith: PATTERN must be a string or cell array of strings"); |
80 pattern = cellstr (pattern); | |
81 endif | 92 endif |
82 | 93 |
83 retval = zeros (size (str)); | |
84 | |
85 ## reverse str and pattern | 94 ## reverse str and pattern |
86 for j = 1:numel (str) | 95 str = cellfun (@flip, cellstr (str), "UniformOutput", false); |
87 str{j} = flip (str{j}); | 96 pattern = cellfun (@flip, cellstr (pattern), "UniformOutput", false); |
88 endfor | |
89 for j = 1:numel (pattern) | |
90 pattern{j} = flip (pattern{j}); | |
91 endfor | |
92 | 97 |
93 if (nargin == 2) | 98 if (nargin == 2) |
94 IgnoreFlag = false; | 99 ignore_case = false; |
95 endif | 100 else |
96 | 101 ## For Matlab compatibility accept any abbreviation of 3rd argument |
97 if (nargin == 4) | 102 if (! ischar (IgnoreCase) || isempty (IgnoreCase) |
98 ## checks third input argument | 103 || ! strncmpi (IgnoreCase, "IgnoreCase", length (IgnoreCase))) |
99 if (! ischar (ignore_case) || isempty (ignore_case)) | |
100 error ('endsWith: third input must be "IgnoreCase"'); | 104 error ('endsWith: third input must be "IgnoreCase"'); |
101 endif | 105 endif |
102 | 106 |
103 ## to be compatible with MATLAB | 107 if (! isscalar (ignore_case) || ! isreal (ignore_case)) |
104 ## (MATLAB accepts the command with "I", "i", "Ig", "IG" ..etc ) | 108 error ('endsWith: "IgnoreCase" value must be a logical scalar'); |
105 if (! strncmpi (ignore_case, "IgnoreCase", length (ignore_case))) | |
106 error (['endsWith: invalid argument "%s"; ', ... | |
107 'third input must be "IgnoreCase"'], ignore_case); | |
108 endif | 109 endif |
109 | 110 ignore_case = logical (ignore_case); |
110 if (numel (confirm_ignore) != 1) | |
111 error ("endsWith: 'IgnoreCase' value must be a logical scalar"); | |
112 endif | |
113 | |
114 ## to be compatible with MATLAB | |
115 ## (MATLAB accepts the command with true, 5, 1 ..etc ) | |
116 try | |
117 IgnoreFlag = logical (confirm_ignore); | |
118 catch | |
119 error ("endsWith: 'IgnoreCase' value must be a logical scalar"); | |
120 end_try_catch | |
121 endif | 111 endif |
122 | 112 |
123 if (IgnoreFlag) | 113 retval = false (size (str)); |
114 if (ignore_case) | |
124 for j = 1:numel (pattern) | 115 for j = 1:numel (pattern) |
125 retval |= strncmpi (str, pattern{j}, length (pattern{j})); | 116 retval |= strncmpi (str, pattern{j}, length (pattern{j})); |
126 endfor | 117 endfor |
127 else | 118 else |
128 for j = 1:numel (pattern) | 119 for j = 1:numel (pattern) |
134 | 125 |
135 | 126 |
136 ## Test simple use with one string and one pattern | 127 ## Test simple use with one string and one pattern |
137 %!assert (endsWith ("hello", "lo")) | 128 %!assert (endsWith ("hello", "lo")) |
138 %!assert (! endsWith ("hello", "LO")) | 129 %!assert (! endsWith ("hello", "LO")) |
139 %!assert (endsWith ("hello", "LO", "i", 5)) # check compatibility with MATLAB | 130 %!assert (endsWith ("hello", "LO", "i", 5)) |
140 %!assert (! endsWith ("hello", "no")) | 131 %!assert (! endsWith ("hello", "no")) |
141 | 132 |
142 ## Test multiple strings with a single pattern | 133 ## Test multiple strings with a single pattern |
143 %!test | 134 %!test |
144 %! str = {"myFile.odt", "results.ppt", "myCode.m"; ... | 135 %! str = {"myFile.odt", "results.ppt", "myCode.m"; ... |
145 %! "data-analysis.ppt", "foundations.txt", "data.odt"}; | 136 %! "data-analysis.ppt", "foundations.txt", "data.odt"}; |
146 %! pattern = ".odt"; | 137 %! pattern = ".odt"; |
147 %! correct_ans = [true, false, false; false, false, true]; | 138 %! expected = [true, false, false; false, false, true]; |
148 %! assert (endsWith (str, pattern), correct_ans) | 139 %! assert (endsWith (str, pattern), expected); |
149 | 140 |
150 ## Test multiple strings with multiple patterns | 141 ## Test multiple strings with multiple patterns |
151 %!test | 142 %!test |
152 %! str = {"tests.txt", "mydoc.odt", "myFunc.m", "results.pptx"}; | 143 %! str = {"tests.txt", "mydoc.odt", "myFunc.m", "results.pptx"}; |
153 %! pattern = {".docx", ".odt", ".txt"}; | 144 %! pattern = {".docx", ".odt", ".txt"}; |
154 %! correct_ans = [true, true, false, false]; | 145 %! expected = [true, true, false, false]; |
155 %! assert (endsWith (str, pattern), correct_ans) | 146 %! assert (endsWith (str, pattern), expected); |
156 | 147 |
157 ## Test IgnoreCase | 148 ## Test IgnoreCase |
158 %!test | 149 %!test |
159 %! str = {"TESTS.TXT", "mydoc.odt", "result.txt", "myFunc.m"}; | 150 %! str = {"TESTS.TXT", "mydoc.odt", "result.txt", "myFunc.m"}; |
160 %! pattern = ".txt"; | 151 %! pattern = ".txt"; |
161 %! correct_ans_with_ignore = [true, false, true, false]; | 152 %! expected_ignore = [true, false, true, false]; |
162 %! correct_ans_without_ignore = [false, false, true, false]; | 153 %! expected_wo_ignore = [false, false, true, false]; |
163 %! assert (endsWith (str, pattern, "IgnoreCase", true), ... | 154 %! assert (endsWith (str, pattern, "IgnoreCase", true), expected_ignore); |
164 %! correct_ans_with_ignore) | 155 %! assert (endsWith (str, pattern, "IgnoreCase", false), expected_wo_ignore); |
165 %! assert (endsWith (str, pattern, "IgnoreCase", false), ... | 156 %! assert (endsWith (str, pattern, "I", 500), expected_ignore); |
166 %! correct_ans_without_ignore) | 157 %! assert (endsWith (str, pattern, "iG", 0), expected_wo_ignore); |
167 %! assert (endsWith (str, pattern, "I", 500), ... | |
168 %! correct_ans_with_ignore) # check compatibility with MATLAB | |
169 %! assert (endsWith (str, pattern, "iG", 0), ... | |
170 %! correct_ans_without_ignore) # check compatibility with MATLAB | |
171 | 158 |
172 ## Test error detection | 159 ## Test input validation |
173 %!error <must be strings or cell arrays of strings> endsWith (152, "hi") | 160 %!error endsWith () |
174 | 161 %!error endsWith ("A") |
175 %!error <must be strings or cell arrays of strings> endsWith ("hello", 152) | 162 %!error endsWith ("A", "B", "C") |
176 | 163 %!error endsWith ("A", "B", "C", "D", "E") |
177 %!error <'IgnoreCase' value must be a logical scalar> ... | 164 %!error <STR must be a string> endsWith (152, "hi") |
178 %! endsWith ("hello", "hi", "i", "true") | 165 %!error <STR must be a .* cell array of strings> endsWith ({152}, "hi") |
179 | 166 %!error <PATTERN must be a string> endsWith ("hi", 152) |
180 %!error <'IgnoreCase' value must be a logical scalar> ... | 167 %!error <PATTERN must be a .* cell array of strings> endsWith ("hi", {152}) |
181 %! endsWith ("hello", "hi", "i", [1, 2]) | 168 %!error <third input must be "IgnoreCase"> endsWith ("hello", "lo", 1, 1) |
182 | 169 %!error <third input must be "IgnoreCase"> endsWith ("hello", "lo", "", 1) |
183 %!error <invalid argument "ignire"; third input must be> ... | 170 %!error <third input must be "IgnoreCase"> endsWith ("hello", "lo", "foo", 1) |
184 %! endsWith ("hello", "he", "ignire", 1) | 171 %!error <"IgnoreCase" value must be a logical scalar> |
185 | 172 %! endsWith ("hello", "hi", "i", "true"); |
186 %!error <invalid argument "IgnoreCasefd"; third input must be> ... | 173 %!error <"IgnoreCase" value must be a logical scalar> |
187 %! endsWith ("hello", "he", "IgnoreCasefd", 1) | 174 %! endsWith ("hello", "hi", "i", {true}); |
188 | |
189 %!error <third input must be "IgnoreCase"> endsWith ("hello", "he", "", 1) | |
190 | |
191 %!error <third input must be "IgnoreCase"> endsWith ("hello", "he", 5, 1) |