读写操作(补充)

读写操作(补充)

之前的一篇有关终端读写的内容比较详细:
https://blog.51cto.com/steed/2315597

这篇,先补充了两个从终端逐行扫描并处理的示例。然后再补充了有关两个写入操作的内容。

bufio包,逐行读取

关于bufio包,使用它可以简便和高效地处理输入和输出。其中一个最有用的特性是称为扫描器(Scanner)的类型,它可以读取输入,以行或者单词为单位断开,这是处理以行为单位输入内容的最简单方式。
先声明一个 bufio.Scanner 类型的变量:

input := bufio.NewScanner(os.Stdin)

扫描器从程序的标准输入进行读取。每一次调用 inout.Scan() 读取下一行,并且将结尾的换行符去掉;通过调用 input.Text() 来获取读到的内容。Scan 方法在读到新行的时候返回true,在没有更多内容的时候返回flase。
下面的例子,从标准输入获取到内容后,转成全大写再打印出来。Windows下使用 Ctrl+Z 后回车可以退出:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    input := bufio.NewScanner(os.Stdin)
    for input.Scan() {
        fmt.Println(strings.ToUpper(input.Text()))
    }
}

上面的示例是每从标准输入获取到一行的内容,就进行处理。还有一种做法是,从标准输入获取全部的内容后(Ctrl+Z 后回车表示输入完成),最后再一次全部输出。下面的示例是从标准输入获取到全部内容,然后全部输出,每一行的内容之间插入一个空格。最后也会有一个空格,输出时把最后一个空格截断:

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "os"
)

func main() {
    input := bufio.NewScanner(os.Stdin)
    buff := bytes.NewBuffer(nil)
    for input.Scan() {
        buff.Write(input.Bytes()) // 忽略错误
        buff.WriteByte(‘ ‘)
    }
    fmt.Printf("%q\n", buff.String()[:buff.Len()-1])  // 截掉最后一个空格
}

读取内容并丢弃

使用io.Copy函数读取响应的内容,比如直接复制内容到标准输出,这样就不需要把数据流装到缓冲区:

n, err := io.Copy(os.Stdout, resp.Body)

还可以通过写入 ioutil.Discard 输出流进行丢弃,这样做应该是为了要有一个读取的过程:

n, err := io.Copy(ioutil.Discard, resp.Body)

向 io.Writer 写入字符串

这里要讲的是通过接口类型断言来查询特性,下面是一个标准库中使用的示例。并且,这个是向 io.Writer 写入字符串的推荐方法。
下面定义一个方法,往 io.Writer 接口接入字符串信息:

func writeMsg(w io.Writer, msg string) error {
    if _, err := w.Write([]byte(msg)); err != nil {
        return err
    }
    return nil
}

因为 Write 方法需要一个字节切片([]byte),而需要写入的是一个字符串,所以要做类型转换。这种转换需要进行内存分配和内存复制,但复制后内存又会被马上抛弃。这里就会有性能问题,这个内存分配会导致性能下降,需要避开这个内存分配。
在很多包中,实现了 io.Writer 的重要类,都会提供一个对应的高效写入字符串的 WriteString 方法,比如:

  • *http.response : 这个类型不可导出,我们一般使用的是 http.ResponseWriter 接口
  • *bytes.Buffer
  • *os.File
  • *bufio.Write

由于 Writer 接口并不包括 WriteString 方法,不能直接调用。这里可以先定义一个新的接口,这个接口只包含 WriteString 方法,然后使用类型断言来判断 w 的动态类型是否满足这个新接口:

// 将s写入w,如果w有WriteString方法,就直接调用该方法
func writeString(w io.Writer, s string) (n int, err error) {
    type stringWriter interface {
        WriteString(string) (n int, err error)
    }
    if sw, ok := w.(stringWriter); ok {
        return sw.WriteString(s) // 避免了内存复制
    }
    return w.Write([]byte(s)) // 分配了临时内存
}

func writeMsg(w io.Writer, msg string) error {
    if _, err := writeString(w, msg); err != nil {
        return err
    }
    return nil
}

由于上面的操作太常见了,io 包已经提供了一个函数 io.WriteString 可以直接使用。上面的代码可以简化为如下的方式:

func writeMsg2(w io.Writer, msg string) error {
    if _, err := io.WriteString(w, msg); err != nil {
        return err
    }
    return nil
}

这里本质上还是一样的,接口的定义、类型检查都封装到了io包的内部,并且内部实现的逻辑和上面是一样的。
之前想要向某个具体的类型写入字符串的时候,会查看该类型是否有 WriteString 方法。但是如果要操作的类型是 io.Writer 接口,虽然实际背后的动态类型还是那个类型,但是就无法调用 WriteString 方法了。这时可以直接使用 io 包提供的工具函数完成同样的操作。

通过 fmt.Fprint 写入

fmt.Fprint 系列的函数,接收的第一个参数是 io.Writer,也可以进行写入操作。而且 fmt 包提供的函数,接收空接口,可以是任何的类型,自带格式化成字符串的功能,很多时候更方便。
在向 os.Stdout 打印输出错误,以及 Web 服务器向客户端发送数据的时候使用的比较多,不过同样的也是可以向任何的 io.Writer 写入内容的,比如写入到文件。

原文地址:https://blog.51cto.com/steed/2390679

时间: 2024-07-30 22:00:01

读写操作(补充)的相关文章

文件的新建、定位、截短和读写操作

1.创建一个新文件,创建新文件除了可以使用open函数之外还可以用creat()函数. 创建文件函数 creat(const char * pathname, mode_t mode) 头文件 :#include <fcntl.h> 参数说明:第一个参数pathname同open函数的第一个参数具有同样的意义,区别在于这是需要创建的文件的地址而不是需要打开文件的地址,第二个参数mode是新建文件的访问权限. 返回值:成功返回1,失败返回-1. 函数说明:creat()函数能够创建一个新的文件,

对SD卡的读写操作-保存用户登录密码

import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import android.content.Context; import android.os.Environment; public class Fileservice { /** * 存储用

oracle读写文件--利用utl_file包对磁盘文件的读写操作

摘要: 用户提出一个需求,即ORACLE中的一个表存储了照片信息,字段类型为BLOB,要求能导出成文件形式. 本想写个C#程序来做,后来想起ORACLE有很多包,功能很好很强大,于是网上参考了些文章完成了. 主要是用了ORACLE的两个包:UTL_FILE和DBMS_LOB. 实现过程: 第一步:以管理员用户登陆设置可操作目录 --CREATE DIRECTORY privilege is granted only to SYS and SYSTEM by default. create or

java文件读写操作类

借鉴了项目以前的文件写入功能,实现了对文件读写操作的封装 仅仅需要在读写方法传入路径即可(可以是绝对或相对路径) 以后使用时,可以在此基础上改进,比如: 写操作: 1,对java GUI中文本框中的内容进行捕获,放在txt文本文档中 2,对各种类型数据都以字符串的形式逐行写入 3,对全局数组的内容进行写入 读操作: 获取文件行数 对逐行字符串型数据进行类型转换,放入二维数组中 为后面算法处理提供入口,但是要小心的是:不可以将行数用全局变量做计数器,否则每次读入是全局变量累加出错,应重新开始读取

顺序表操作补充(查找方法增加)

顺序表操作补充 二分查找 a.非递归方法实现二分查找 1 //[] 2 int BinarySearch(SeqList *pSeq, ElemType x) 3 { 4 assert(pSeq); 5 int left = 0; 6 int right = pSeq->size - 1; 7 while(left<=right) 8 { 9 int cur = left+(right-left)/2; 10 if(pSeq->array[cur] == x) 11 { 12 retur

python excel读写操作

1.读操作 xlrd 下载地址:https://pypi.python.org/pypi/xlrd 使用代码 # encoding : utf-8 #设置编码方式 import xlrd #导入xlrd模块 #打开指定文件路径的excel文件 xlsfile = r'D:\AutoPlan\apisnew.xls' book = xlrd.open_workbook(xlsfile) #获得excel的book对象 #获取sheet对象,方法有2种: sheet_name=book.sheet_

配置文件读写操作类

1 package com.epichust.mestar.client.utils; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.util.Properties; 8 9 /** 10 * 读取配置文件工具类 11 * 12 * 13 */ 14 public cl

php学习基础-文件系统(二) 文件读写操作、文件资源处理

一.文件的打开与关闭 /* *读取文件中的内容 * file_get_contents(); //php5以上 * file() * readfile(); * * 不足:全部读取, 不能读取部分,也不能指定的区域 * * fopen() * fread() * fgetc() * fgets() * * * * * 写入文件 * file_put_contents("URL", "内容字符串"); //php5以上 * 如果文件不存在,则创建,并写入内容 * 如果

使用shell脚本简单模拟对特定文件同时读写操作

使用shell脚本简单模拟对特定文件同时读写操作文件内容的格式:field1    ,       field2    , field3    ,       field4以,为分隔符,但是存在空格. 脚本用法如下: ./check_write_read.sh 10 输出结果: Thu Apr 27 19:59:44 CST 2017:Read operation finished 670 Thu Apr 27 19:59:44 CST 2017:Write operation finished