Swift—文本输出流

print是Swift标准库中最经常使用的函数之一。实际上,这是程序员在编写“Hello,world!”时学习的第一个函数。使人惊讶的是,咱们不多有人熟悉其余形式。html

例如,您是否知道实际的签名print是 print(_:separator:terminator:)?或者它有一个名为print(_:separator:terminator:to:)?的变体 ?git

使人震惊,我知道。程序员

这就像了解你最好的朋友*“Chaz”* 的中间名,而且他的完整法定名称其实是 “R”。巴克敏斯特小查尔斯拉格兰德“ - 哦,并且,他们一直都有一个彻底相同的双胞胎。github

一旦你花了一些时间来收集本身,请继续阅读,找出你以前认为不须要进一步介绍的功能的所有真相。swift


让咱们首先仔细看看以前的函数声明:api

func print<Target>(_ items: Any...,
                   separator: String = default,
                   terminator: String = default,
                   to output: inout Target)
    where Target : TextOutputStream

复制代码

这个重载print 采用可变长度的参数列表,后跟separatorterminator参数 - 二者都有默认值。bash

  • separator是用于将每一个元素的表示链接items 成单个字符串的字符串。默认状况下,这是一个空格(" ")。
  • terminator是附加到打印表示的末尾的字符串。默认状况下,这是换行符(\ n "\n")。

最后一个参数output 采用Target符合协议的泛型类型的可变实例。Text<wbr style="box-sizing: border-box;">Output<wbr style="box-sizing: border-box;">Streamapp

符合的类型的实例 能够传递给函数以从标准输出中捕获和重定向字符串。Text<wbr style="box-sizing: border-box;">Output<wbr style="box-sizing: border-box;">Stream``print(_:to:)框架

实现自定义文本输出流类型

因为Unicode的多变性,您没法经过查看字符串来了解字符串中潜伏的字符。在 组合标记, 格式字符, 不支持的字符, 变体序列, 连字,有向图和其余表现形式之间,单个扩展字形集群能够包含远远超过眼睛的东西。ide

举个例子,让咱们建立一个符合的自定义类型。咱们不会逐字地将字符串写入标准输出,而是检查每一个组成代码点Text<wbr style="box-sizing: border-box;">Output<wbr style="box-sizing: border-box;">Stream

符合协议只是知足方法要求的问题。Text<wbr style="box-sizing: border-box;">Output<wbr style="box-sizing: border-box;">Stream``write(_:)

protocol TextOutputStream {
    mutating func write(_ string: String)
}

复制代码

在咱们的实现中,咱们迭代Unicode.Scalar传递的字符串中的每一个值; 的enumerated()收集方法提供当前在每次循环偏移。在方法的顶部,guard若是字符串为空或换行符,则语句会提早解除(这会减小控制台中的噪音量)。

struct UnicodeLogger: TextOutputStream {
    mutating func write(_ string: String) {
        guard !string.isEmpty && string != "\n" else {
            return
        }

        for (index, unicodeScalar) in
            string.unicodeScalars.lazy.enumerated()
        {
            let name = unicodeScalar.name ?? ""
            let codePoint = String(format: "U+%04X", unicodeScalar.value)
            print("\(index): \(unicodeScalar) \(codePoint)\t\(name)")
        }
    }
}

复制代码

要使用咱们的新类型,请初始化它并将其分配给变量(with ),以便它能够做为参数传递。任什么时候候咱们想要得到字符串的X射线而不是仅仅打印它的表面表示,咱们能够在咱们的声明中添加一个额外的参数。Unicode<wbr style="box-sizing: border-box;">Logger``var``inout``print

这样作可让咱们揭示关于表情符号字符的秘密👨👩👧👧:它其实是 由ZWJ字符加入的四个单独表情符号的 序列 - 总共七个代码点!

print("👨‍👩‍👧‍👧")
// Prints: "👨‍👩‍👧‍👧"

var logger = UnicodeLogger()
print("👨‍👩‍👧‍👧", to: &logger)
// Prints:
// 0: 👨 U+1F468    MAN
// 1:    U+200D     ZERO WIDTH JOINER
// 2: 👩 U+1F469    WOMAN
// 3:    U+200D     ZERO WIDTH JOINER
// 4: 👧 U+1F467    GIRL
// 5:    U+200D     ZERO WIDTH JOINER
// 6: 👧 U+1F467    GIRL

复制代码

在Swift 5.0中,您能够经过其Unicode properties属性访问标量值的名称。与此同时,咱们可使用 字符串变换 来为咱们提取名称(咱们只须要在两端去掉一些残骸)。

import Foundation

extension Unicode.Scalar {
    var name: String? {
        guard var escapedName =
                "\(self)".applyingTransform(.toUnicodeName,
                                            reverse: false)
        else {
            return nil
        }

        escapedName.removeFirst(3) // remove "\\N{"
        escapedName.removeLast(1) // remove "}"

        return escapedName
    }
}

复制代码

有关更多信息,请参阅 SE-0211:“将Unicode属性添加到Unicode.Scalar”

使用自定义文本输出流的想法

如今咱们知道Swift标准库的一个不起眼的部分,咱们能够用它作什么?

事实证实,有不少潜在的用例。为了更好地了解它们是什么,请考虑如下示例:Text<wbr style="box-sizing: border-box;">Output<wbr style="box-sizing: border-box;">Stream

记录到标准错误

默认状况下,Swift print语句指向 标准输出(stdout。若是您但愿改成指向 标准error(stderr,则能够建立新的文本输出流类型并按如下方式使用它:

import func Darwin.fputs
import var Darwin.stderr

struct StderrOutputStream: TextOutputStream {
    mutating func write(_ string: String) {
        fputs(string, stderr)
    }
}

var standardError = StderrOutputStream()
print("Error!", to: &standardError)

复制代码

将输出写入文件

前面的写入示例stderr 能够归纳为写入任何流或文件,而是经过建立输出流 (能够经过类型属性访问标准错误)。File<wbr style="box-sizing: border-box;">Handle

import Foundation

struct FileHandlerOutputStream: TextOutputStream {
    private let fileHandle: FileHandle
    let encoding: String.Encoding

    init(_ fileHandle: FileHandle, encoding: String.Encoding = .utf8) {
        self.fileHandle = fileHandle
        self.encoding = encoding
    }

    mutating func write(_ string: String) {
        if let data = string.data(using: encoding) {
            fileHandle.write(data)
        }
    }
}

复制代码

按照这种方法,您能够自定义print写入文件而不是流。

let url = URL(fileURLWithPath: "/path/to/file.txt")
let fileHandle = try FileHandle(forWritingTo: url)
var output = FileHandlerOutputStream(fileHandle)

print("\(Date())", to: &output)

复制代码

转发流输出

做为最后一个例子,让咱们想象一下你会发现本身常常将控制台输出复制粘贴到某个网站上的表单中的状况。不幸的是,该网站有试图解析无益的行为<,并>就好像它们是HTML。

每次发布到网站时,您均可以建立一个 自动处理该文本的内容,而不是采起额外的步骤来逃避文本(在这种状况下,咱们使用咱们发现深埋在Core Foundation中的XML转义函数)。Text<wbr style="box-sizing: border-box;">Output<wbr style="box-sizing: border-box;">Stream

import Foundation

struct XMLEscapingLogger: TextOutputStream {
    mutating func write(_ string: String) {
        guard !string.isEmpty && string != "\n",
            let xmlEscaped = CFXMLCreateStringByEscapingEntities(nil, string as NSString, nil)
        else {
            return
        }

        print(xmlEscaped)
    }
}

var logger = XMLEscapingLogger()
print("<3", to: &logger)
// Prints "&lt;3"

复制代码

对于开发人员来讲,打印是一种熟悉且便捷的方式,能够了解其代码的行为。它补充了更全面的技术,如日志框架和调试器,而且 - 在Swift的状况下 - 证实它自己就很是强大。


扫码进交流群 有技术的来闲聊 没技术的来学习

691040931

原文转载地址:nshipster.com/textoutputs…

相关文章
相关标签/搜索