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)