Swift—文本输出流

栏目: Swift · 发布时间: 5年前

内容简介:例如,您是否知道实际的签名令人震惊,我知道。

print 是Swift标准库中最常用的函数之一。实际上,这是 程序员 在编写“Hello,world!”时学习的第一个函数。令人惊讶的是,我们很少有人熟悉其他形式。

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

令人震惊,我知道。

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

一旦你花了一些时间来收集自己,请继续阅读,找出你之前认为不需要进一步介绍的功能的全部真相。

让我们首先仔细看看之前的函数声明:

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

复制代码

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

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

最后一个参数 output 采用 Target 符合协议的泛型类型的可变实例。 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``print(_:to:)

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

由于Unicode的多变性,您无法通过查看字符串来了解字符串中潜伏的字符。在组合标记,  格式字符 ,  不支持的字符 ,  变体序列 ,  连字,有向图和其他表现形式之间 ,单个扩展字形集群可以包含远远超过眼睛的东西。

举个例子,让我们创建一个符合的自定义类型。我们不会逐字地将字符串写入标准输出,而是检查每个组成 代码点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

这样做可以让我们揭示关于表情符号字符的秘密:man::woman::girl::girl::它实际上是 由 ZWJ 字符加入的四个单独表情符号的 序列-  总共七个代码点!

print(":family_man_woman_girl_girl:")
// Prints: ":family_man_woman_girl_girl:"

var logger = UnicodeLogger()
print(":family_man_woman_girl_girl:", to: &logger)
// Prints:
// 0: :man: U+1F468    MAN
// 1:    U+200D     ZERO WIDTH JOINER
// 2: :woman: U+1F469    WOMAN
// 3:    U+200D     ZERO WIDTH JOINER
// 4: :girl: U+1F467    GIRL
// 5:    U+200D     ZERO WIDTH JOINER
// 6: :girl: 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 "<3"

复制代码

对于开发人员来说,打印是一种熟悉且便捷的方式,可以了解其代码的行为。它补充了更全面的技术,如日志框架和调试器,并且 - 在Swift的情况下 - 证明它本身就非常强大。


以上所述就是小编给大家介绍的《Swift—文本输出流》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Node即学即用

Node即学即用

[英] Tom Hughes-Croucher、[英] Mike Wilson / 郑达韡 / 人民邮电出版社 / 2013-2 / 39.00元

《Node即学即用》由休斯-克劳奇、威尔逊编著,《Node即学即用》讲解如何用Node构建可扩展因特网应用,是全面的实用指南,除了详细介绍Node提供的API外,还用大量篇幅介绍了服务器事件驱动开发的重要概念。内容涉及跨服务器的并发连接、非阻塞I/O和事件驱动的编程、如何支持各种数据库和数据存储工具、NodeAPI的使用示例等。适合对JavaScript及编程有一定程度了解的读者阅读。一起来看看 《Node即学即用》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具