> Thanks for the tip on print(String(validatingUTF8: buf)), that does
> reproduce my input text, line for line, EXCEPT for wrapping every line in
> "Optional(line-of-text-with-terminator)", for example "Optional("import
> Glibc\n")".
>
> So, how does wrapping a line of UTF8 text in another character string
> "Optional()" help me print the text? Is Optional() some kind of function?
> If so, how is it intended to be used?
The inclusion of "Optional(…)" in the printout is meant to tell you that what
you got wasn't a plain `String`, but an `Optional<String>`.
`Optional` is a type Swift uses to express that a certain value might be `nil`.
Essentially, an `Optional<String>` either contains a `String`, or it doesn't
contain a `String`, in which case it is `nil`. To print the `Optional<String>`,
you must first extract the `String` from it in some way, which is called
"unwrapping" it.
(Note: Although I've written the type `Optional<String>` out longhand, Swift
has a very commonly used shorthand form: `String?`. This is what you'll see in
most API listings.)
In this case, the `String(validatingUTF8:)` constructor returns an
`Optional<String>` instead of just a `String` because, if the bytes you pass it
are not valid UTF-8, it returns `nil` instead of returning an invalid string.
If you want to use the `String`, you need to decide how to unwrap it, and thus
how to handle the `nil` case where the UTF-8 you passed in was invalid.
Getting down to concrete code changes, what you need to do here is first
separate the conversion to a `String` from the `print()` line:
while fgets(&buf, Int32(BUFSIZE), file_handle) != nil
{
let possibleString = String(validatingUTF8: buf)
print(possibleString)
}
And then use one of Swift's features for unwrapping Optional types. There are
three simple ways you can choose from:
1. You can use the postfix `!` operator to extract the value from
`possibleString`, failing an assertion (i.e. crashing) if `possibleString`
happens to be `nil`. That sounds a little bit ridiculous, but if you have
complete control of the input data and you know it should *never* contain
invalid UTF-8, crashing is probably better than adding a code path to your
program that you haven't really thought about because it "can't happen".
while fgets(&buf, Int32(BUFSIZE), file_handle) != nil
{
let possibleString = String(validatingUTF8: buf)
print(possibleString!)
}
2. You can use the infix `??` operator to substitute a default value for the
string. For instance, you might decide to print "[invalid]" for invalid text:
while fgets(&buf, Int32(BUFSIZE), file_handle) != nil
{
let possibleString = String(validatingUTF8: buf)
print(possibleString ?? "[invalid]")
}
3. You can use the `if let` construct to test for whether `possibleString` has
a value and, if so, use it. For instance, if you just want to skip invalid
lines:
while fgets(&buf, Int32(BUFSIZE), file_handle) != nil
{
let possibleString = String(validatingUTF8: buf)
if let string = possibleString {
print(string)
}
}
Or if you wanted to print a message to `stderr` and exit with a nonzero return
code:
while fgets(&buf, Int32(BUFSIZE), file_handle) != nil
{
let possibleString = String(validatingUTF8: buf)
if let string = possibleString {
print(string)
}
else {
fprintf(stderr, "Invalid UTF-8 byte sequence")
exit(1)
}
}
This second behavior, incidentally, might be better written as a `guard let`
statement. `guard` is sort of the reverse of `if`—it has only an `else` block.
The `else` block is also *required* to exit the scope it's in, for instance by
calling `exit`, using `return`, failing an assertion, or (since we're in a
loop) using `continue` or `break`.
while fgets(&buf, Int32(BUFSIZE), file_handle) != nil
{
let possibleString = String(validatingUTF8: buf)
guard let string = possibleString else {
fprintf(stderr, "Invalid UTF-8 byte sequence")
exit(1)
}
print(string)
}
You can see how the `guard` statement lets you leave the "happy path"
unindented, whereas the equivalent `if` forced you to indent it. If you had
several different conditions—the line must be valid, the line must not be
empty, the line must contain only digits, etc.—then using `guard` for each of
these conditions might make your code significantly more readable than it would
be as a series of nested `if`s.
--
Brent Royal-Gordon
Architechies
_______________________________________________
swift-users mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-users