Go程序设计语言1.3 找出重复行-阿里云开发者社区

开发者社区> 华章计算机> 正文

Go程序设计语言1.3 找出重复行

简介:
+关注继续查看

1.3 找出重复行


用于文件复制、打印、检索、排序、统计的程序,通常有一个相似的结构:在输入接口上循环读取,然后对每一个元素进行一些计算,在运行时或者在最后输出结果。我们展示三个版本的dup程序,它受UNIX的uniq命令启发来找到相邻的重复行。这个程序使用容易适配的结构和包。

第一个版本的dup程序输出标准输入中出现次数大于1的行,前面是次数。这个程序引入if语句、map类型和bufio包。

 

 

像for一样,if语句中的条件部分也从不放在圆括号里面,但是程序体中需要用到大括号。这里还可以有一个可选的else部分,当条件为false的时候执行。

map存储一个键/值对集合,并且提供常量时间的操作来存储、获取或测试集合中的某个元素。键可以是其值能够进行相等(==)比较的任意类型,字符串是最常见的例子;值可以是任意类型。这个例子中,键的类型是字符串,值是int。内置的函数make可以用来新建map,它还可以有其他用途。map将在4.3节中进行更多讨论。

每次dup从输入读取一行内容,这一行就作为map中的键,对应的值递增1。语句counts[input.Text()]++等价于下面的两个语句:

 

键在map中不存在时也是没有问题的。当一个新的行第一次出现时,右边的表达式counts[line]根据值类型被推演为零值,int的零值是0。

为了输出结果,我们使用基于range的for循环,这次在map类型的counts变量上遍历。像以前一样,每次迭代输出两个结果,map里面一个元素对应的键和值。map里面的键的迭代顺序不是固定的,通常是随机的,每次运行都不一致。这是有意设计的,以防止程序依赖某种特定的序列,此处不对排序做任何保证。

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

程序使用短变量的声明方式,新建一个bufio.Scanner类型input变量:

 

扫描器从程序的标准输入进行读取。每一次调用input.Scan()读取下一行,并且将结尾的换行符去掉;通过调用input.Text()来获取读到的内容。Scan函数在读到新行的时候返回true,在没有更多内容的时候返回false。

像C语言或其他语言中的printf一样,函数fmt.Printf从一个表达式列表生成格式化的输出。它的第一个参数是格式化指示字符串,由它指定其他参数如何格式化。每一个参数的格式是一个转义字符、一个百分号加一个字符。例如:%d将一个整数格式化为十进制的形式,%s把参数展开为字符串变量的值。

Printf函数有超过10个这样的转义字符,Go程序员称为verb。下表远不完整,但是它说明有很多可以用的功能:

verb 描述

%d 十进制整数

%x,%o,%b 十六进制、八进制、二进制整数

%f,%g,%e 浮点数:如3.141593, 3.141592653589793, 3.141593e+00

%t 布尔型:true或false

%c 字符(Unicode码点)

%s 字符串

%q 带引号字符串(如"abc")或者字符(如'c')

%v 内置格式的任何值

%T 任何值的类型

%% 百分号本身(无操作数)

 

程序dup1中的格式化字符串还包含一个制表符\t和一个换行符\n。字符串字面量可以包含类似转义序列(escape sequence)来表示不可见字符。Printf默认不写换行符。按照约定,诸如log.Printf和fmt.Errorf之类的格式化函数以f结尾,使用和fmt.Printf相同的格式化规则;而那些以ln结尾的函数(如Println)则使用%v的方式来格式化参数,并在最后追加换行符。

许多程序既可以像dup一样从标准输入进行读取,也可以从具体的文件读取。下一个版本的dup程序可以从标准输入或一个文件列表进行读取,使用os.Open函数来逐个打开:

 

 

函数os.Open返回两个值。第一个是打开的文件(*os.File),该文件随后被Scanner读取。

第二个返回值是一个内置的error类型的值。如果err等于特殊的内置nil值,标准文件成功打开。文件在被读到结尾的时候,Close函数关闭文件,然后释放相应的资源(内存等)。另一方面,如果err不是nil,说明出错了。这时,error的值描述错误原因。简单的错误处理是使用Fprintf和%v在标准错误流上输出一条消息,%v可以使用默认格式显示任意类型的值;错误处理后,dup开始处理下一个文件;continue语句让循环进入下一个迭代。

为了保持示例代码简短,这里对错误处理有意进行了一定程度的忽略。很明显,必须检查os.Open返回的错误;但是,我们忽略了使用input.Scan读取文件的过程中出现概率很小的错误。我们将标记所跳过的错误检查,5.4节将更详细地讨论错误处理。

值得注意的是,对countLines的调用出现在其声明之前。函数和其他包级别的实体可以以任意次序声明。

map是一个使用make创建的数据结构的引用。当一个map被传递给一个函数时,函数接收到这个引用的副本,所以被调用函数中对于map数据结构中的改变对函数调用者使用的map引用也是可见的。在示例中,countLines函数在counts map中插入的值,在main函数中也是可见的。

这个版本的dup使用“流式”模式读取输入,然后按需拆分为行,这样原理上这些程序可以处理海量的输入。一个可选的方式是一次读取整个输入到大块内存,一次性地分割所有行,然后处理这些行。接下去的版本dup3将以这种方式处理。这里引入一个ReadFile函数(从io/ioutil包),它读取整个命名文件的内容,还引入一个strings.Split函数,它将一个字符串分割为一个由子串组成的slice。(Split是前面介绍过的strings.Join的反操作。)

我们在某种程度上简化了dup3:第一,它仅读取指定的文件,而非标准输入,因为ReadFile需要一个文件名作为参数;第二,我们将统计行数的工作放回main函数中,因为它当前仅在一处用到。

 

ReadFile函数返回一个可以转化成字符串的字节slice,这样它可以被strings.Split分割。3.5.4节将详细讨论字符串和字节slice。

实际上,bufio.Scanner、ioutil.ReadFile以及ioutil.WriteFile使用*os.File中的Read

和Write方法,但是大多数程序员很少需要直接访问底层的例程。像bufio和io/ioutil包中上层的方法更易使用。

练习1.4:修改dup2程序,输出出现重复行的文件的名称。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器ECS远程登录用户名密码查询方法
阿里云服务器ECS远程连接登录输入用户名和密码,阿里云没有默认密码,如果购买时没设置需要先重置实例密码,Windows用户名是administrator,Linux账号是root,阿小云来详细说下阿里云服务器远程登录连接用户名和密码查询方法
3493 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
8268 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
9513 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
5838 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
3664 0
10059
文章
0
问答
来源圈子
更多
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载