# Introducing Julia/Strings and characters
## Strings and characters
### Strings

A string is a sequence of one or more characters, usually found enclosed in double quotes:
There are two important things you need to know about strings.
One is, that they're immutable. You can't change them once they're created. But it's easy to make new strings from parts of existing ones.
The second is that you have to be careful when using two specific characters: double quotes ("), and dollar signs ($). If you want to include a double quote character in the string, it has to be preceded with a backslash, otherwise the rest of the string would be interpreted as Julia code, with potentially interesting results. And if you want to include a dollar sign ($) in a string, that should also be prefaced by a backslash, because it's used for string interpolation.
Strings can also be enclosed in triple double quotes. This is useful because you can use ordinary double quotes inside the string without having to put backslashes before them:
You'll encounter a few specialized types of string too, which consist of one or more characters immediately followed by the opening double quote:
* r" " indicates a regular expression
* v" " indicates a version string
* b" " indicates a byte literal
* raw" " indicates a raw string that doesn't do interpolation

### String interpolation

You often want to use the results of Julia expressions inside strings. For example, suppose you want to say:
where n is the current value of x. Any Julia expression can be inserted into a string with the $ construction:
You don't have to use the parentheses if you're just using the name of a variable:
To include the result of a Julia expression in a string, enclose the expression in parentheses first, then precede it with a dollar sign:

### Substrings

To extract a smaller string from a string, use getindex(s, range) or s[range] syntax. For basic ASCII strings, you can use the same techniques that you use to extract elements from arrays:
which is equivalent to:
You can easily iterate through a string:
Watch out if you take a single element from the string, rather than a string of length 1 (i.e. with the same start and end positions):
The second result isn't a string, but a character (inside single quotes).

#### Unicode strings

Not all strings are ASCII. To access individual characters in Unicode strings, you can't always use simple indexing, because some characters occupy more than one index position. Don't be fooled just because some of the index numbers appear to work:
Instead of length(str) to find the length of a string, use lastindex(str):
The isascii functions tests whether a string is ASCII or contains Unicode characters:
In this string, the 'second' character, é, has 2 bytes, the 'fourth' character, 𐅍, has 4 bytes.
The 'third' character, B, starts with the 4th element in the string.
You can also do this even more easily using the pairs function:
As an alternative, use the eachindex iterator:
There are other useful functions for working with strings like this, including collect, thisind, nextind, and prevind:

### Splitting and joining strings

You can stick strings together (a process often called concatenation) using the multiply (*) operator:
If you've used other programming languages, you might expect to use the addition (+) operator:
- so use *.
If you can 'multiply' strings, you can also raise them to a power:
You can also use string:
but if you want to do a lot of concatenation, inside a loop, perhaps, it might be better to use the string buffer approach (see below).
To split a string, use split function. Given this simple string:
a simple call to the split function divides the string at the spaces, returning a five-piece array:
Or you can specify the string of 1 or more characters to split at:
The characters you use to do the splitting don't appear in the final result:
If you want to split a string into separate single-character strings, use the empty string ("") which splits the string between the characters:
You can also split strings using a regular expression to define the splitting points. Use the special regex string construction r" ". Inside this, you can use regular expression characters with special meanings:
Here, the r"a|e|i|o|u" is a regular expression string, and — as you'll know if you love regular expressions — that this matches any of the vowels. So the resulting array consists of the string split at every vowel. Notice the empty strings in the results -— if you don't want those, add a false flag at the end:
If you wanted to keep the vowels, rather than use them for splitting work, you have to delve deeper into the world of regex literal strings. Read on.
You can join the elements of a split string in array form using join:

#### Splitting using a function

Many functions in Julia let you use functions as part of a function call. Anonymous functions are useful, because you can make function calls which have smart choices built-in. For example, split lets you provide a function in place of the delimiter character. In the next example, the delimiter is (bizarrely) specified to be any upper-case character whose ASCII code is a multiple of 8:

### Character objects

Above we extracted smaller strings from larger strings:
But when we extracted a single element from a string:
note the single quotes. In Julia, these are used to mark character objects, so 'a' is a character object, but "a" is a string with length 1. These are not equivalent.
You can convert character objects to strings easily enough:
or
It's easy to input 32 bits Unicode characters using \U escape sequence (the uppercase means 32 bits). The lowercase escape sequence \u can be used for 16 and 8 bit characters:
For strings, the \Uxxxxxxxx and \uxxxx syntax is more strict.

### Converting between numbers and strings

Turning integers into strings is the job of the string function. The keyword base lets you specify the number base for the conversion, which you can use to convert decimal digits to a binary, octal, or hexadecimal string:
To convert strings to numbers, use parse, and you can also specify the number base (such as binary or hex) if you want the string to be interpreted as using a number base:

### Converting characters to integers and back again

Int converts a character into an integer, and Char turns an integer into a character.
To go from a single character string to the code number (such as its ASCII or UTF code number), try this:
For a quick alphabet:

### printf formatting

If you're deeply attached to C-style printf functionality, you'll be able to use a Julia macro (you call macros by prefacing them with the @ sign). The macro is provided in the Printf package, which you'll need to load first:
or you can create another string using the sprintf macro, also to be found in the Printf package:

### Convert a string to an array

To read from a string into an array, you can use the IOBuffer function. This is available with a number of Julia functions (including printf). Here's a string of data (it could have been read from a file):
Now you can "read" this string using functions such as readdlm, the "read with delimiters" function. This can be found in the package DelimitedFiles.
You can add an optional type specification:
Sometimes you want to do things to strings that you can do better with arrays. Here's an example.
You can explode the pathname string into an array of character objects, using collect, which gathers the items in a collection or string into an array:
Similarly, you can use split to split the string and count the results:
To count the occurrences of a particular character object, you can use an anonymous function:
although here converting to an array is unnecessary and inefficient. Here's a better way:

### Finding and replacing things inside strings

If you want to know whether a string contains a specific character, use the general-purpose in function.
But the occursin function, which accepts two strings, is more generally useful, because you can use substrings with one or more characters. Notice that you place the search term first, then the string you're looking in — occursin(needle, haystack):
You can get the location of the first occurrence of a substring using findfirst(needle, haystack). The first argument can be a single character, a string, or a regular expression:
In each case, the result contains the indices of the characters, if present.

#### Replacing

The replace function returns a new string with a substring of characters replaced with something else:
You use the => operator to specify the pattern you're looking for, and its replacement. Usually the third argument is another string, as here. But you can also supply a function that processes the result:
where the function (here, the built-in uppercase function) is applied to the matching substring.
There's no replace! function, where the "!" indicates a function that changes its argument. That's because you can't change a string — they're immutable.

##### Replacing using functions

Many functions in Julia allow you to supply functions as part of the function call, and you can make good use of anonymous functions for this. Here, for example, is how to use a function to provide random replacements in a replace function.

### Regular expressions

You can use regular expressions to find matches for substrings. Some functions that accept a regular expression are:
* replace changes occurrences of regular expressions
* match returns the first match or nothing
* eachmatch returns an iterator that lets you search through all matches
* split splits a string at every match
Use replace to replace each consonant with an underscore:
and the following code replaces each vowel with the results of running a function on each match:
With replace you can access the matches if you provide a special substitution string s"", where \1 refers to the first match, \2 to the second, and so on. With this regex operation, each lowercase letter preceded by a space is repeated three times:
For more regular expression fun, there are the -match- functions.
Here I've loaded the complete text of "The Adventures of Sherlock Holmes" from a file into the string called text:
To use the possibility of a match as a Boolean condition, suitable for use in an if statement for example, use occursin.
That's odd. We were expecting to find evidence of the great detective's peculiar pharmacological recreations. In fact, the word "opium" does appear in the text, but only in lower-case, hence this false result—regular expressions are case-sensitive.
This is a case-insensitive search, set by the flag (?i)), and it returns true.
You could check every line for the word using a simple loop:
For more useable output (in the REPL), add enumerate and some highlighting:
There's an alternative syntax for adding regex modifiers, such as case-insensitive matches. Notice the "i" immediately following the regex string in the second example:
With the eachmatch function, you apply the regex to the string to produce an iterator. For example, to look for substrings in our text matching the letters "L", followed by some other characters, ending with "ed":
The result in lmatch is an iterable object containing all the matches, as RegexMatch objects:
We can step through the iterator and look at each match in turn. You can access a number of fields of a RegexMatch, to extract information about the match. These include captures, match, offset, offsets, and regex. For example, the match field contains the matched substring:
Other fields include captures, the captured substrings as an array of strings, offset, the offset into the string at which the whole match begins, and offsets, the offsets of the captured substrings.
To get an array of matching strings, use something like this:
The basic match function looks for the first match for your regex. Use the match field to extract the information from the RegexMatch object:
A more streamlined way of obtaining matching lines from a file is this:

#### Making a Regex

Sometimes you want to make a regular expression from within your code. You can do this by making a Regex object. Here is one way you could count the number of vowels in the text:

##### Making a substitution string

Sometimes you'll want to assemble a substitution string. To do this, you can use SubstitutionString instead of s"...".
For example, say you want to do some string interpolation in the replacement string. Perhaps you have a list of files, and you want to renumber them, so that "file2.png" becomes "file1.png":
Notice that you can't simply use \1 in the SubstitutionString to refer to the first captured expression, you have to escape it as \\1, and use \g (escaped as \\g) to refer to the named capture group.

### Testing and changing strings

There are lots of functions for testing and changing strings:
* length(str) length of string
* sizeof(str) length/size
* startswith(strA, strB) does strA start with strB?
* endswith(strA, strB) does strA end with strB?
* occursin(strA, strB) does strA occur in strB?
* all(isletter, str) is str entirely letters?
* all(isnumeric, str) is str entirely number characters?
* isascii(str) is str ASCII?
* all(iscntrl, str) is str entirely control characters?
* all(isdigit, str) is str 0-9?
* all(ispunct, str) does str consist of punctuation?
* all(isspace, str) is str whitespace characters?
* all(isuppercase, str) is str uppercase?
* all(islowercase, str) is str entirely lowercase?
* all(isxdigit, str) is str entirely hexadecimal digits?
* uppercase(str) return a copy of str converted to uppercase
* lowercase(str) return a copy of str converted to lowercase
* titlecase(str) return copy of str with the first character of each word converted to uppercase
* uppercasefirst(str) return copy of str with first character converted to uppercase
* lowercasefirst(str) return copy of str with first character converted to lowercase
* chop(str) return a copy with the last character removed
* chomp(str) return a copy with the last character removed only if it's a newline

### Streams

To write to a string, you can use a Julia stream. The sprint (String Print) function lets you use a function as the first argument, and uses the function and the rest of the arguments to send information to a stream, returning the result as a string.
For example, consider the following function, f. The body of the function maps an anonymous 'print' function over the arguments, enclosing them with angle brackets. When used by sprint, the function f processes the remaining arguments and sends them to the stream.
Functions like println can take an IOBuffer or stream as their first argument. This lets you print to streams instead of printing to the standard output device:
After this, the in-memory stream called iobuffer is full of numbers and newlines, even though nothing was printed on the terminal. To copy the contents of iobuffer from the stream to a string or array, you can use take!:

### Colored / Styled Output

The following prints out messages in their respective colors using printstyled:

### Printing a formatted Backtrace

In the middle of a try catch statement, the following will print the original backtrace that caused the exception:
If you are outside a try-catch and want to print a stacktrace without throwing stopping execution use this: