Sign in Join
Wiki copytemporary

macOS NSRegularExpression: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Regular expressions
22
33Regular expressions are patterns used to match specified alpha-numeric character combinations in the string data being searched.
44
55Each character in a regular expression (that is, each character in the string describing its pattern) is either a metacharacter or operator, having a special meaning, or a regular character that has a literal meaning.
66
77Regular expressions can be incredibly complex. Indeed, whole books have been written about them! For a gentle introduction to regular expressions, see this [O'Reilly article](https://www.oreilly.com/content/an-introduction-to-regular-expressions/).
88
99## NSRegularExpression Overview
1010
1111The NSRegularExpression class has convenience methods for returning all the matches as an array, the total number of matches, the first match, and the range of the first match.
1212
1313An individual match is represented by an instance of the NSTextCheckingResult class, which carries information about the overall matched range (via its range property), and the range of each individual capture group (via the rangeAtIndex method).
1414
1515NSRegularExpression conforms to the International Components for Unicode (ICU) specification for regular expressions.
1616
1717### Metacharacters
1818
1919For a comprehensive list of characters used by the NSRegularExpression class that have a special meaning in regular expression patterns, see the [ICU listing](https://unicode-org.github.io/icu/userguide/strings/regexp.html#regular-expression-metacharacters).
2020
2121### Operators
2222
2323For a comprehensive list of operators used by the NSRegularExpression class, see the [ICU listing](https://unicode-org.github.io/icu/userguide/strings/regexp.html#regular-expression-operators).
2424
2525## Example 1 - match a pattern
2626
2727In this fairly trivial and contrived code example, we use the **\d** metacharacter which matches a decimal digit and the **+** operator to match the preceding decimal digit one or more times. This pattern **\d+** aims to match all the occurrences of numbers in the search string which we then output using NSLog(). It uses the NSRegularExpression convenience methods for returning all of the matches in the search string as an array and the total number of matches.
2828
2929### Code
3030
3131``` pascal
3232Program regex_ex1;
3333
3434{$mode objfpc}{$H+}
3535{$modeswitch objectivec2}
3636
3737Uses
3838 MacOSAll, CocoaAll, SysUtils;
3939
4040Var
4141 srchStr : String;
4242 patnStr : String;
4343 myRegex : NSregularExpression;
4444 matches : NSArray;
4545 match : NSTextCheckingResult;
4646 error : NSErrorPtr;
4747
4848Begin
4949 error := Nil;
5050 srchStr := 'I have 43 bags of 60 marbles.';
5151 patnStr := '\d+';
5252
5353 // Create a regular expression with given string and options
5454 myRegex := NSregularExpression.regularExpressionWithPattern_options_error(NSStr(patnStr), NSRegularExpressionOptions(NSRegularExpressionCaseInsensitive), error);
5555
5656 // Check creation of regular expression with given string and options
5757 if(error <> Nil) then
5858 begin
5959 NSLog(NSStr('Regex creation error: %@'), error);
6060 Exit;
6161 end;
6262
6363 // Save any matches in the given string in the matches array
6464 matches := myRegex.matchesInString_options_range(NSStr(srchStr), 0, NSMakeRange(0, srchStr.Length));
6565
6666 // Output
6767 NSLog(NSStr('Search string: %@'), NSStr(srchStr));
6868 NSLog(NSStr('Pattern string: %@'), NSStr(patnStr));
6969 NSLog(NSStr('Number of matches: %lu'), myRegex.numberOfMatchesInString_options_range(NSStr(srchStr), 0, NSMakeRange(0, srchStr.Length)));
7070
7171 for match in matches do
7272 NSLog(NSStr('match: %@'), NSStr(srchStr).substringWithRange(match.rangeAtIndex(0)));
7373End.
7474```
7575
7676### Output
7777
7878The output from running the above code example is:
7979
80-`2021-06-12 21:05:46.335 regex_ex1[26138:232243] Search string: I have 43 bags of 60 marbles.`\
81-`2021-06-12 21:05:46.336 regex_ex1[26138:232243] Pattern string: \d+`\
82-`2021-06-12 21:05:46.336 regex_ex1[26138:232243] Number of matches: 2`\
83-`2021-06-12 21:05:46.336 regex_ex1[26138:232243] match: 43`\
84-`2021-06-12 21:05:46.336 regex_ex1[26138:232243] match: 60`
80+```text
81+2021-06-12 21:05:46.335 regex_ex1[26138:232243] Search string: I have 43 bags of 60 marbles.
82+2021-06-12 21:05:46.336 regex_ex1[26138:232243] Pattern string: \d+
83+2021-06-12 21:05:46.336 regex_ex1[26138:232243] Number of matches: 2
84+2021-06-12 21:05:46.336 regex_ex1[26138:232243] match: 43
85+2021-06-12 21:05:46.336 regex_ex1[26138:232243] match: 60
86+```
8587
8688### Code explanation
8789
8890The call to *regularExpressionWithPattern_options_error()* creates an NSRegularExpression object instance (myRegex) with the specified regular expression pattern and options.
8991
9092Options are specified using *NSRegularExpressionOptions()*. Note that by default NSRegularExpression performs case-sensitive searches, so we specified the NSRegularExpressionCaseInsensitive option for case-insenstive searches although, because we are dealing with digits above, this has no effect and we might as well have specified Nil in this example for no options.
9193
9294Once we have the NSRegularExpression object, we can then use it for matching text among other operations.
9395
9496After checking that the creation of the regular expression did not fail with an error, we call the *matchesInString_options_range()* method to search for any matches and store them in our NSArray (matches). This method takes our search string, any options (there are none here) and the range to search. The range is specified by giving *NSMakeRange()* the starting location to search in the string (0 = the beginning of the string) and the length of the search string.
9597
9698Next, we output the search string and pattern string, and then call the *numberOfMatchesInString_options_range()* method to determine the number of matches and output it.
9799
98100Finally, we iterate through the matches NSArray and output the matches individually. The call to *rangeAtIndex(0)* is the full match and is equivalent to simply calling *range*. The code for this looks a little obscure. Let me try to unpack it for you.
99101
100102If you just output the content of the matches NSArray you get this:
101103
102-`"<NSSimpleRegularExpressionCheckingResult: 0x14d617570>{7, 2}{<NSRegularExpression: 0x14d614fd0> \\d+ 0x1}",`\
103-`"<NSSimpleRegularExpressionCheckingResult: 0x14d617610>{18, 2}{<NSRegularExpression: 0x14d614fd0> \\d+ 0x1}"`
104+```text
105+"<NSSimpleRegularExpressionCheckingResult: 0x14d617570>{7, 2}{<NSRegularExpression: 0x14d614fd0> \\d+ 0x1}",
106+"<NSSimpleRegularExpressionCheckingResult: 0x14d617610>{18, 2}{<NSRegularExpression: 0x14d614fd0> \\d+ 0x1}"
107+```
104108
105109Notice the {7, 2} and {18, 2} ranges which locate the first number at position 7 (counting from zero) in the search string with a length of 2 and the second number at position 18 with a length of 2. Knowing those ranges, you could use:
106110
107111``` pascal
108112NSLog(NSStr('match: ''%@'''), NSStr(srchStr).substringWithRange(NSMakeRange(7, 2)));
109113```
110114
111115to output the first number. The *substringWithRange()* method extracts from our search string the substring that matches the specified range (7, 2). Clearer than mud? I hope so.
112116
113117## Example 2 - match pattern groups
114118
115119This is similar to Example 1 above, except that this time we match groups of characters. Our search string is the same as before, but our pattern string has some added complexity. The pattern matches a decimal digit one or more times as before, but this time as a group which is delineated by using parentheses: **(/d+)**. Next, we use a point **.** to match any character and **?** to match zero or one times following the digit(s). Finally, we match the set of characters from a to z **(\[a-z\]+)** one or more times as a group.
116120
117121### Code
118122
119123``` pascal
120124Program regex_ex2;
121125
122126{$mode objfpc}{$H+}
123127{$modeswitch objectivec2}
124128
125129Uses
126130 MacOSAll, CocoaAll, SysUtils;
127131
128132Var
129133 srchStr : String;
130134 patnStr : String;
131135 myRegex : NSregularExpression;
132136 matches : NSArray;
133137 match : NSTextCheckingResult;
134138 error : NSErrorPtr;
135139
136140Begin
137141 error := Nil;
138142 srchStr := 'I have 43 Bags of 60 Marbles.';
139143 patnStr := '(\d+).?([a-z]+)';
140144
141145 // Create a regular expression with given string and options
142146 myRegex := NSregularExpression.regularExpressionWithPattern_options_error(NSStr(patnStr), NSRegularExpressionOptions(NSRegularExpressionCaseInsensitive), error);
143147
144148 // Check creation of regular expression with given string and options
145149 if(error <> Nil) then
146150 begin
147151 NSLog(NSStr('Regex creation error: %@'), error);
148152 Exit;
149153 end;
150154
151155 // Save any matches in the given string in the matches array
152156 matches := myRegex.matchesInString_options_range(NSStr(srchStr), 0, NSMakeRange(0, srchStr.Length));
153157
154158 // Output
155159 NSLog(NSStr('Search string: %@'), NSStr(srchStr));
156160 NSLog(NSStr('Pattern string: %@'), NSStr(patnStr));
157161 NSLog(NSStr('Number of matches: %lu'), myRegex.numberOfMatchesInString_options_range(NSStr(srchStr), 0, NSMakeRange(0, srchStr.Length)));
158162
159163 for match in matches do
160164 begin
161165 NSLog(NSStr('match(0): ''%@'''), NSStr(srchStr).substringWithRange(match.rangeAtIndex(0)));
162166 NSLog(NSStr('match(1): ''%@'''), NSStr(srchStr).substringWithRange(match.rangeAtIndex(1)));
163167 NSLog(NSStr('match(2): ''%@'''), NSStr(srchStr).substringWithRange(match.rangeAtIndex(2)));
164168 end;
165169End.
166170```
167171
168172### Output
169173
170-`2021-06-14 17:39:51.255 program2[7376:149163] Search string: I have 43 bags of 60 marbles.`\
171-`2021-06-14 17:39:51.255 program2[7376:149163] Pattern string: (\d+).?([a-z]+)`\
172-`2021-06-14 17:39:51.255 program2[7376:149163] Number of matches: 2`\
173-`2021-06-14 17:39:51.255 program2[7376:149163] match(0): '43 Bags'`\
174-`2021-06-14 17:39:51.255 program2[7376:149163] match(1): '43'`\
175-`2021-06-14 17:39:51.255 program2[7376:149163] match(2): 'Bags'`\
176-`2021-06-14 17:39:51.255 program2[7376:149163] match(0): '60 Marbles'`\
177-`2021-06-14 17:39:51.255 program2[7376:149163] match(1): '60'`\
178-`2021-06-14 17:39:51.255 program2[7376:149163] match(2): 'Marbles'`
174+```text
175+2021-06-14 17:39:51.255 program2[7376:149163] Search string: I have 43 bags of 60 marbles.
176+2021-06-14 17:39:51.255 program2[7376:149163] Pattern string: (\d+).?([a-z]+)
177+2021-06-14 17:39:51.255 program2[7376:149163] Number of matches: 2
178+2021-06-14 17:39:51.255 program2[7376:149163] match(0): '43 Bags'
179+2021-06-14 17:39:51.255 program2[7376:149163] match(1): '43'
180+2021-06-14 17:39:51.255 program2[7376:149163] match(2): 'Bags'
181+2021-06-14 17:39:51.255 program2[7376:149163] match(0): '60 Marbles'
182+2021-06-14 17:39:51.255 program2[7376:149163] match(1): '60'
183+2021-06-14 17:39:51.255 program2[7376:149163] match(2): 'Marbles'
184+```
179185
180186### Code explanation
181187
182188The explanation is pretty much the same as for Example 1, except that:
183189
1841901\) The NSRegularExpressionCaseInsensitive option for case-insenstive searches now has a use. We specified the set of lowercase characters a to z, but because of the option we matched the words Bags and Marbles with initial capital letters.
185191
1861922\) The call to *rangeAtIndex(0)* or the equivalent *range* matches the full pattern; the call to *rangeAtIndex(1)* matches the first group of digits that we specified; and the call to *rangeAtIndex(2)* matches the second group of characters that we specified.
187193
188194## Example 3 - replace matched pattern
189195
190196This is similar to Example 1 above, except that this time we replace the decimal numbers matched by the regular expression with words instead of numbers. This is done two ways: one uses the *stringByReplacingMatchesInString_options_range_withTemplate()* method which returns a new string with the replacements and the other uses the *replaceMatchesInString_options_range_withTemplate()* method which replaces the matches in the original search string.
191197
192198### Code
193199
194200``` pascal
195201Program regex_ex3;
196202
197203{$mode objfpc}{$H+}
198204{$modeswitch objectivec2}
199205
200206Uses
201207 MacOSAll, CocoaAll, SysUtils;
202208
203209Var
204210 srchStr : NSString;
205211 srchStr2: NSMutableString;
206212 patnStr : NSString;
207213 templStr: NSString;
208214 myRegex : NSregularExpression;
209215 matches : NSArray;
210216 match : NSTextCheckingResult;
211217 numMatch: NSInteger;
212218 error : NSErrorPtr;
213219 count : ShortInt;
214220
215221Begin
216222 error := Nil;
217223 srchStr := NSStr('I have 43 Bags of 60 Marbles.');
218224 srchStr2:= NSMutableString.stringWithString(srchStr);
219225 patnStr := NSStr('\d+');
220226
221227 // Create a regular expression with given string and options
222228 myRegex := NSregularExpression.regularExpressionWithPattern_options_error(patnStr, NSRegularExpressionOptions(NSRegularExpressionCaseInsensitive), error);
223229
224230 // Check creation of regular expression with given string and options
225231 if(error <> Nil) then
226232 begin
227233 NSLog(NSStr('Regex creation error: %@'), error);
228234 Exit;
229235 end;
230236
231237 // Save any matches in the given string in the matches array
232238 matches := myRegex.matchesInString_options_range(srchStr, 0, NSMakeRange(0, srchStr.Length));
233239
234240 // Save number of matches
235241 numMatch := myRegex.numberOfMatchesInString_options_range(srchStr, 0, NSMakeRange(0, srchStr.Length));
236242
237243 // Output
238244 NSLog(NSStr('Search string: %@'), srchStr);
239245 NSLog(NSStr('Pattern string: %@'), patnStr);
240246 NSLog(NSStr('Number of matches: %lu'), numMatch);
241247
242248 // Alternative 1: Using stringByReplacingMatchesInString_options_range_withTemplate()
243249 for count := numMatch downto 1 do
244250 begin
245251 for match in matches do
246252 begin
247253 if(srchStr.substringWithRange(match.range) = NSStr('43')) then
248254 begin
249255 templStr := NSStr('forty-three');
250256 NSLog(NSStr('Match 1: %@ - Template string: %@'), srchStr.substringWithRange(match.range), templStr);
251257 end;
252258
253259 if(srchStr.substringWithRange(match.range) = NSStr('60')) then
254260 begin
255261 templStr := NSStr('sixty');
256262 NSLog(NSStr('Match 2: %@ - Template string: %@'), srchStr.substringWithRange(match.range), templStr);
257263 end;
258264
259265 // Do string replacement
260266 srchStr := myRegex.stringByReplacingMatchesInString_options_range_withTemplate(srchStr, NSRegularExpressionOptions(Nil), match.range, templStr);
261267 end;
262268
263269 // Save any matches in the new search string in the matches array
264270 matches := myRegex.matchesInString_options_range(srchStr, 0, NSMakeRange(0, srchStr.Length));
265271 end;
266272
267273 // Output string with replacements
268274 NSLog(NSStr('Result: %@'), srchStr);
269275
270276 // Save any matches in the given string in the matches array
271277 matches := myRegex.matchesInString_options_range(srchStr2, 0, NSMakeRange(0, srchStr2.Length));
272278
273279 // Alternative 2: Using replaceMatchesInString_options_range_withTemplate()
274280 for count := numMatch downto 1 do
275281 begin
276282 for match in matches do
277283 begin
278284 if(srchStr2.substringWithRange(match.range) = NSStr('43')) then
279285 begin
280286 templStr := NSStr('forty-three');
281287 NSLog(NSStr('Match 1: %@ - Template string: %@'), srchStr2.substringWithRange(match.range), templStr);
282288 end;
283289 if(srchStr2.substringWithRange(match.range) = NSStr('60')) then
284290 begin
285291 templStr := NSStr('sixty');
286292 NSLog(NSStr('Match 2: %@ - Template string: %@'), srchStr2.substringWithRange(match.range), templStr);
287293 end;
288294
289295 // Do string replacement
290296 myRegex.replaceMatchesInString_options_range_withTemplate(srchStr2, NSRegularExpressionOptions(Nil), match.range, templStr);
291297 end;
292298
293299 // Save any matches in the new search string in the matches array
294300 matches := myRegex.matchesInString_options_range(srchStr2, 0, NSMakeRange(0, srchStr2.Length));
295301 end;
296302
297303 // Output string with replacements
298304 NSLog(NSStr('Result: %@'), srchStr2);
299305End.
300306```
301307
302308### Output
303309
304-`2021-06-14 22:42:55.807 program3[9115:227765] Search string: I have 43 Bags of 60 Marbles.`\
305-`2021-06-14 22:42:55.808 program3[9115:227765] Pattern string: \d+`\
306-`2021-06-14 22:42:55.808 program3[9115:227765] Number of matches: 2`\
307-`2021-06-14 22:42:55.808 program3[9115:227765] Match 1: 43 - Template string: forty-three`\
308-`2021-06-14 22:42:55.808 program3[9115:227765] Match 2: 60 - Template string: sixty`\
309-`2021-06-14 22:42:55.808 program3[9115:227765] Result: I have forty-three Bags of sixty Marbles.`\
310-`2021-06-14 22:42:55.808 program3[9115:227765] Match 1: 43 - Template string: forty-three`\
311-`2021-06-14 22:42:55.808 program3[9115:227765] Match 2: 60 - Template string: sixty`\
312-`2021-06-14 22:42:55.808 program3[9115:227765] Result: I have forty-three Bags of sixty Marbles.`
310+```text
311+2021-06-14 22:42:55.807 program3[9115:227765] Search string: I have 43 Bags of 60 Marbles.
312+2021-06-14 22:42:55.808 program3[9115:227765] Pattern string: \d+
313+2021-06-14 22:42:55.808 program3[9115:227765] Number of matches: 2
314+2021-06-14 22:42:55.808 program3[9115:227765] Match 1: 43 - Template string: forty-three
315+2021-06-14 22:42:55.808 program3[9115:227765] Match 2: 60 - Template string: sixty
316+2021-06-14 22:42:55.808 program3[9115:227765] Result: I have forty-three Bags of sixty Marbles.
317+2021-06-14 22:42:55.808 program3[9115:227765] Match 1: 43 - Template string: forty-three
318+2021-06-14 22:42:55.808 program3[9115:227765] Match 2: 60 - Template string: sixty
319+2021-06-14 22:42:55.808 program3[9115:227765] Result: I have forty-three Bags of sixty Marbles.
320+```
313321
314322## Example 4 - rearrange matched pattern groups
315323
316324This is similar to Example 2 above, except that this time not only do we match groups of characters, we rearrange them. Our search string is different this time and represents a contact list. We are going to rearrange the format from Firstname, Lastname to the more sensible Lastname, Firstname and then enclose the contact number state area code in parentheses. To do this, our pattern string has some added complexity.
317325
318326The pattern matches the first word group as a group of word characters followed by a comma **(\w+),** and does exactly the same again for the second word group, and finally matches the two digits of the area code as a group **(\d{2})**. The curly braces enclosing the number **{2}** after the digit metacharacter specifies that we want to match exactly two digits which comprise the area code.
319327
320328The template string rearranges the data groups by switching the order of the first two word groups and adding parentheses around the third group which comprises the area code number.
321329
322330### Code
323331
324332``` pascal
325333Program regex_ex4;
326334
327335{$mode objfpc}{$H+}
328336{$modeswitch objectivec2}
329337
330338Uses
331339 MacOSAll, CocoaAll, SysUtils;
332340
333341Var
334342 srchStr : NSString;
335343 srchStr2: NSMutableString;
336344 patnStr : NSString;
337345 tmplStr : NSString;
338346 myRegex : NSregularExpression;
339347 matches : NSArray;
340348 match : NSTextCheckingResult;
341349 error : NSErrorPtr;
342350
343351Begin
344352 error := Nil;
345353 srchStr := NSStr('Firstname, Lastname, 02 9428 4687');
346354 srchStr2:= NSMutableString.stringWithString(srchStr);
347355 patnStr := NSStr('(\w+), (\w+), (\d{2})');
348356 tmplStr := NSStr('$2, $1, ($3)');
349357
350358 // Create a regular expression with given string and options
351359 myRegex := NSregularExpression.regularExpressionWithPattern_options_error(patnStr, NSRegularExpressionOptions(NSRegularExpressionCaseInsensitive), error);
352360
353361 // Check creation of regular expression with given string and options
354362 if(error <> Nil) then
355363 begin
356364 NSLog(NSStr('Regex creation error: %@'), error);
357365 Exit;
358366 end;
359367
360368 // Save any matches in the given string in the matches array
361369 matches := myRegex.matchesInString_options_range(srchStr, 0, NSMakeRange(0, srchStr.Length));
362370
363371 // Output
364372 NSLog(NSStr('Search string: %@'), srchStr);
365373 NSLog(NSStr('Pattern string: %@'), patnStr);
366374 NSLog(NSStr('Template string: %@'), tmplStr);
367375 NSLog(NSStr('Number of matches: %lu'), myRegex.numberOfMatchesInString_options_range(srchStr, 0, NSMakeRange(0, srchStr.Length)));
368376
369377 for match in matches do
370378 begin
371379 NSLog(NSStr('match(1): %@'), srchStr.substringWithRange(match.rangeAtIndex(1)));
372380 NSLog(NSStr('match(2): %@'), srchStr.substringWithRange(match.rangeAtIndex(2)));
373381 NSLog(NSStr('match(3): %@'), srchStr.substringWithRange(match.rangeAtIndex(3)));
374382 // Do the replacements
375383 myRegex.replaceMatchesInString_options_range_withTemplate(srchStr2, NSRegularExpressionOptions(Nil), match.range, tmplStr);
376384 end;
377385
378386 NSLog(NSStr('Result: %@'), srchStr2);
379387End.
380388```
381389
382390### Output
383391
384-`2021-06-15 20:37:54.880 program4[23076:159228] Search string: Firstname, Lastname, 02 9428 4687`\
385-`2021-06-15 20:37:54.880 program4[23076:159228] Pattern string: (\w+), (\w+), (\d{2})`\
386-`2021-06-15 20:37:54.880 program4[23090:159932] Template string: $2, $1, ($3)`\
387-`2021-06-15 20:37:54.881 program4[23076:159228] Number of matches: 1`\
388-`2021-06-15 20:37:54.881 program4[23076:159228] match(1): Firstname`\
389-`2021-06-15 20:37:54.881 program4[23076:159228] match(2): Lastname`\
390-`2021-06-15 20:37:54.881 program4[23076:159228] match(3): 02`\
391-`2021-06-15 20:37:54.881 program4[23076:159228] Result: Lastname, Firstname, (02) 9428 4687`
392+```text
393+2021-06-15 20:37:54.880 program4[23076:159228] Search string: Firstname, Lastname, 02 9428 4687
394+2021-06-15 20:37:54.880 program4[23076:159228] Pattern string: (\w+), (\w+), (\d{2})
395+2021-06-15 20:37:54.880 program4[23090:159932] Template string: $2, $1, ($3)
396+2021-06-15 20:37:54.881 program4[23076:159228] Number of matches: 1
397+2021-06-15 20:37:54.881 program4[23076:159228] match(1): Firstname
398+2021-06-15 20:37:54.881 program4[23076:159228] match(2): Lastname
399+2021-06-15 20:37:54.881 program4[23076:159228] match(3): 02
400+2021-06-15 20:37:54.881 program4[23076:159228] Result: Lastname, Firstname, (02) 9428 4687
401+```
392402
393403## See also
394404
395405- [[macOS NSString Regular Expressions]].
396406- [[RegEx packages]] - cross platform.
397407
398408## External links
399409
400410- [Apple: NSRegularExpression](https://developer.apple.com/documentation/foundation/nsregularexpression).
401411
402412- [ICU: Documentation](https://unicode-org.github.io/icu/).
403413
404414---
405415*From the Free Pascal wiki, page "macOS NSRegularExpression", as it stood on 2021-08-07 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history