> 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
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to