Go语言的内置包使您能够在应用程序中执行各种文件操作,从写入、读取和创建文件到创建和删除目录。当执行文件操作时遇到错误时,它还会提供有用的错误消息。
在 Go 中读取文件
os包提供了一个ReadFile使 Go 中读取文件变得简单的函数。例如,我的项目文件夹中有一个data.txt文件,我可以使用以下代码读取并打印它:
package main
import ( "fmt" "os" )
func main() { filepath := "data.txt" data, err := os.ReadFile(filepath) if err != nil { fmt.Println("File reading error", err) return } fmt.Println(string(data)) }
|
- 上面的代码定义了一个 filepath 变量,该变量的字符串值是我要读取的文件 data.txt。
- 然后使用 ReadFile 函数读取文件,并将结果和错误存储在 data 和 err 变量中。
- 最后,在使用字符串辅助函数将结果以字符串格式打印出来之前,它会检查是否有错误。
用 Go 逐行读取日志文件
在本节中,我们将探讨在 Go 中读取文件的一个实际用例。假设您正在构建一个帮助用户部署 Go 应用程序的应用程序。您需要为用户提供一种查看部署错误的方法,以便他们在尝试重新部署应用程序之前了解需要解决哪些问题。
我们将编写一段代码,逐行读取日志文件,并只打印用户要求的行数。这样,用户就可以选择只查看最后五条日志,而不必翻阅所有日志来查找部署中的问题。
我有一个 log.txt 文件示例,看起来是这样的:
2023-07-11 10:00:00 - Successful: operation completed. 2023-07-11 10:05:12 - Error: Failed to connect to the database. 2023-07-11 10:10:32 - Successful: data retrieval from API. 2023-07-11 10:15:45 - Error: Invalid input received. 2023-07-11 10:20:58 - Successful: file upload. 2023-07-11 10:25:01 - Error: Authorization failed. 2023-07-11 10:30:22 - Successful: record update. 2023-07-11 10:35:37 - Error: Internal server error. 2023-07-11 12:45:59 - Error: Server overloaded. 2023-07-11 12:50:06 - Successful: session created. 2023-07-11 12:55:17 - Error: Invalid input parameters. 2023-07-11 13:00:30 - Successful: software update installed. 2023-07-11 13:05:46 - Error: Access denied. 2023-07-11 13:10:53 - Successful: report generated. 2023-07-11 13:16:01 - Error: Unexpected exception occurred. 2023-07-11 13:20:13 - Successful: user registration. 2023-07-11 13:25:28 - Error: Disk read/write failure.
|
代码如下:
package main
import ( "bufio" "fmt" "os" )
func printLastNLines(lines []string, num int) []string { var printLastNLines []string for i := len(lines) - num; i < len(lines); i++ { printLastNLines = append(printLastNLines, lines[i]) } return printLastNLines }
func main() { filepath := "log.txt" file, err := os.Open(filepath) if err != nil { fmt.Println("Error opening file:", err) } defer file.Close()
var lines []string scanner := bufio.NewScanner(file) for scanner.Scan() { lines = append(lines, scanner.Text())
} if err := scanner.Err(); err != nil { fmt.Println("Error reading file:", err) }
// print the last 10 lines of the file printLastNLines := printLastNLines(lines, 3) for _, line := range printLastNLines { fmt.Println(line) fmt.Println("________") } }
|
上面的代码:
- 使用 os 的包 Open 函数打开文件,
- 用 defer 关键字延迟关闭函数,
- 定义一个空的 lines slice,并使用 bufio 的 NewScanner 函数逐行读取文件,
- 同时使用 Text 函数将每一行以文本格式追加到 lines 数组中。
- 最后,使用 printLastNLines 函数获取行数组的最后 N 行。N 是用户选择的任意数字,在本例中是 3,
- 代码使用 for 循环打印每一行,每一行之间用横线隔开。
使用示例 log.txt 文件,上述代码应返回以下内容:
2023-07-11 13:16:01 - Error: Unexpected exception occurred. ________ 2023-07-11 13:20:13 - Successful: user registration. ________ 2023-07-11 13:25:28 - Error: Disk read/write failure. ________
|
在 Go 中读取 .json 文件
读取和使用 .json 文件中的数据也是编程中的一种常用情况,让我们来学习一下如何做到这一点。例如,你有一个如下所示的 .json 配置文件:
{
"database_host": "localhost", "database_port": 5432, "database_username": "myuser", "database_password": "mypassword", "server_port": 8080, "server_debug": true, "server_timeout": 30
}
|
您还需要通过读取和解码 .json 文件来读取信息,以便应用程序正常运行:
package main
import ( "encoding/json" "fmt" "os" )
type Config struct { DBHost string `json:"database_host"` DBPort int `json:"database_port"` DBUsername string `json:"database_username"` DBPassword string `json:"database_password"` ServerPort int `json:"server_port"` ServerDebug bool `json:"server_debug"` ServerTimeout int `json:"server_timeout"` }
func main() { filepath := "config.json" var config Config
file, err := os.Open(filepath) if err != nil { fmt.Println(err) } defer file.Close()
decoder := json.NewDecoder(file) err = decoder.Decode(&config) if err != nil { fmt.Println(err) } fmt.Println(config) }
|
上面的代码使用 os 的包 Open 函数打开文件,用 defer 关键字延迟关闭函数,并使用 json 的 NewDecoder 函数读取文件。然后,它使用 Decoder 函数将文件解码为对象,并在终端打印配置详细信息之前检查错误:
{localhost 5432 myuser mypassword 8080 true 30}
在 Go 中读取 .csv 文件
逗号分隔值(CSV)是最流行的文件格式之一。让我们在本节中探讨如何读取以下 .csv 文件:
Name,Email,Phone,Address John Doe,johndoe@example.com,555-1234,123 Main St Jane Smith,janesmith@example.com,555-5678,456 Elm St Bob Johnson,bobjohnson@example.com,555-9876,789 Oak St
|
代码:
package main
import ( "encoding/csv" "fmt" "os" )
func main() { filepath := "data.csv"
file, err := os.Open(filepath) if err != nil { fmt.Println(err) } defer file.Close()
reader := csv.NewReader(file) records, err := reader.ReadAll() if err != nil { fmt.Println(err) }
fmt.Println(records) }
|
上面的代码使用 os 的包 Open 函数打开文件,使用 defer 关键字延迟关闭函数,并创建一个阅读器变量来存储 NewReader 函数的结果。然后使用 ReadAll 函数读取文件、检查错误并将结果打印到终端。输出结果应该是这样的
[[Name Email Phone Address] [John Doe johndoe@example.com 555-1234 123 Main St] [Jane Smith janesmith@example.com 555-5678 456 Elm St] [Bob Johnson bobjohnson@example.com 555-9876 789 Oak St]]
|
用 Go 从文件中读取字节数
在某些情况下,您可能希望用 Go 从文件中读取特定数量的字节:
package main
import ( "fmt" "os" )
func main() { filepath := "data.txt"
file, err := os.Open(filepath) if err != nil { fmt.Println(err) } defer file.Close()
data := make([]byte, 10) _, err = file.Read(data) if err != nil { fmt.Println(err) } fmt.Println(data) fmt.Println(string(data)) }
|
上面的代码使用 os 软件包的 Open 函数打开文件,用 defer 关键字延迟关闭函数,并使用 make 函数创建一个值为 10 字节的数据变量。然后在以字节和字符串格式打印数据前检查是否有错误:
[49 46 32 102 109 116 10 50 46 32] 1. fmt 2.
|
用 Go 写操作文件
在 Go 中读写文件相对简单,因为它为开发人员提供了大量执行此类任务的函数,而无需下载第三方库。在本节中,我们将探讨不同的 Go 编写任务以及如何正确处理它们。
在 Go 中创建文件
Go 的 os 包提供了一个 Create 函数,可以创建任意扩展名的文件。例如,假设您有一个帮助用户部署应用程序的应用程序。在这种情况下,您可能希望在用户创建新项目后立即创建一个包含应用程序日志的 log.txt 空文件:
package main
import ( "fmt" "log" "os" )
func main() { file, err := os.Create("log.txt") if err != nil { log.Fatal(err) } defer file.Close() fmt.Print("File created successfully") }
|
上面的代码使用操作系统的 Create 函数创建一个空的 log.txt 文件,检查是否有任何错误,然后向用户打印一条成功信息。
Go 中的文件打开标志
Go 提供的文件打开标志由 os 包中定义的常量表示。这些标志决定了文件操作的行为,例如打开、创建和截断文件。下面列出了这些标志及其作用。
- os.O_RDONLY:以只读方式打开文件。文件必须存在。
- os.O_WRONLY:以只写方式打开文件。如果文件存在,其内容将被截断。如果文件不存在,则创建一个新文件。
- os.O_RDWR: 打开文件供读写。如果文件存在,其内容将被截断。如果不存在,则创建一个新文件。
- os.O_APPEND:写入时向文件添加数据。写入发生在文件末尾。
- os.O_CREATE:如果文件不存在,则创建一个新文件。
- os.O_EXCL:与 O_CREATE 一起使用,确保只创建文件,防止在文件已存在的情况下创建文件。
- os.O_SYNC:为同步 I/O 操作打开文件。写操作在调用返回前完成。
- os.O_TRUNC:如果文件存在并被成功打开,其内容将被截断为零长度。
- os.O_NONBLOCK: 以非阻塞模式打开文件。如果没有可用数据或操作会阻塞,读取或写入等操作可能会立即返回错误。
这些标志可以用位运算符 OR(|)组合起来。例如,os.O_WRONLY|os.O_CREATE 将打开文件供写入,如果文件不存在,则创建文件。
使用这些标志时,重要的是要检查文件操作返回的错误,以处理无法按预期打开或创建文件的情况。
让我们在下一节看看如何向文件写入文本。
在 Go 中将文本写入文件
os 软件包还提供了一个 WriteString 函数,可以帮助你将字符串写入文件。例如,你想用一条日志信息更新 log.txt 文件:
package main
import ( "log" "os" )
func main() { file, err := os.OpenFile("log.txt", os.O_WRONLY|os.O_CREATE, 0644) if err != nil { log.Fatal(err) } defer file.Close()
data := "2023-07-11 10:05:12 - Error: Failed to connect to the database. _________________" _, err = file.WriteString(data) if err != nil { log.Fatal(err) }
}
|
上面的代码使用 OpenFile 函数以只写模式打开 log.txt 文件,如果该文件不存在,则创建该文件。然后创建一个包含字符串的数据变量,并使用 WriteString 函数将字符串数据写入文件。
在 Go 中追加文件
上一节中的代码在每次运行代码写入新数据之前都会删除文件中的数据,这在某些情况下是可以接受的。但是,对于日志文件,您希望它保留所有以前的日志,这样用户就可以根据需要多次引用它们,例如执行分析。
package main
import ( "log" "os" )
func main() { file, err := os.OpenFile("log.txt", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { log.Fatal(err) } defer file.Close()
data := "\n 2023-07-11 10:05:12 - Error: Failed to connect to the database.\n __________________ \n" _, err = file.WriteString(data) if err != nil { log.Fatal(err) }
}
|
上述代码使用 os.O_APPEND 以追加模式打开文件,并在向 log.txt 文件添加新数据之前保留所有现有数据。每次运行代码时,你都会得到一个更新的文件,而不是一个新文件。
用 Go 将字节写入文件
Go 允许使用 Write 函数将字节以字符串形式写入文件。例如,如果您从服务器流式传输数据,而服务器返回的是字节,您可以将字节写入文件以便读取:
package main
import ( "log" "os" )
func main() { file, err := os.OpenFile("data.bin", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { log.Fatal(err) } defer file.Close()
data := []byte{0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0A} _, err = file.Write(data) if err != nil { log.Fatal(err) }
}
|
上述代码以只写和追加模式打开 data.bin 文件,并在该文件不存在的情况下创建它。上面的代码应返回一个包含以下内容的 data.bin 文件:Hello, World!
用 Go 将格式化数据写入文件
这是构建软件应用程序时最常见的文件编写任务之一。例如,如果您正在创建一个电子商务网站,您需要为每个买家创建订单确认收据,其中将包含用户订单的详细信息。下面介绍如何在 Go 中实现这一功能:
package main
import ( "fmt" "log" "os" )
func main() { username, orderNumber := "Adams_adebayo", "ORD6543234" file, err := os.Create(username + orderNumber + ".pdf") if err != nil { log.Fatal(err) } defer file.Close() item1, item2, item3 := "shoe", "bag", "shirt" price1, price2, price3 := 1000, 2000, 3000
_, err = fmt.Fprintf(file, "Username: %s\nOrder Number: %s\nItem 1: %s\nPrice 1: %d\nItem 2: %s\nPrice 2: %d\nItem 3: %s\nPrice 3: %d\n", username, orderNumber, item1, price1, item2, price2, item3, price3) if err != nil { log.Fatal(err) }
}
|
上面的代码定义了两个变量 username 和 orderNumber,根据变量创建了一个 .pdf,检查是否有错误,并用 defer 关键字延迟 Close 函数。然后定义 item1、item2 和 item3 三个变量,用 fmt 的 Fprintf 格式化所有变量的信息,并将其写入 .pdf 文件。
上述代码创建了一个 Adams_adebayoORD6543234.pdf 文件,内容如下:
Username: Adams_adebayo Order Number: ORD6543234 Item 1: shoe Price 1: 1000 Item 2: bag Price 2: 2000 Item 3: shirt Price 3: 3000
|
用 Go 写入 .csv 文件
在编码/csv 软件包的帮助下,您可以用 Go 轻松地将数据写入 .csv 文件。例如,您想在新用户注册后将其个人资料信息存储到 .csv 文件中:
package main
import ( "encoding/csv" "log" "os" )
func main() { file, err := os.OpenFile("users.csv", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { log.Fatal(err) } defer file.Close()
writer := csv.NewWriter(file) defer writer.Flush() data := []string{"Adams Adebayo", "30", "Lagos"} err = writer.Write(data) if err != nil { log.Fatal(err) }
}
|
上面的代码以只写和追加模式打开 users.csv 文件,并在文件不存在的情况下创建该文件。然后,它将使用 NewWriter 函数创建一个写入器变量,推迟执行 Flush 函数,使用字符串片段创建一个数据变量,并使用 Write 函数将数据写入文件。
用 Go 将 JSON 数据写入文件
将 JSON 数据写入 .json 文件是软件开发中的常见用例。例如,您正在构建一个小型应用程序,并希望使用一个简单的 .json 文件来存储应用程序数据:
package main
import ( "encoding/json" "log" "os" )
func main() { file, err := os.OpenFile("users.json", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { log.Fatal(err) } defer file.Close()
data := map[string]interface{}{ "username": "jdon", "twitter": "@jdon", "email": "hello@jdon.com", "website": "https://jdon,com/", "location": "Lagos, Nigeria", }
encoder := json.NewEncoder(file) err = encoder.Encode(data) if err != nil { log.Fatal(err) }
}
|
上面的代码以只写和追加模式打开 users.csv 文件,并在文件不存在的情况下创建该文件,延迟关闭函数,并定义一个包含用户数据的数据变量。然后使用 NewEncoder 函数创建一个编码器变量,并使用 Encoder 函数对其进行编码。
在 Go 中将 XML 数据写入文件
您还可以使用 encoding/xml 软件包在 Go 中将 XML 数据写入文件:
package main
import ( "encoding/xml" "log" "os" )
func main() { type Person struct { Name string `xml:"name"` Age int `xml:"age"` City string `xml:"city"` }
file, err := os.OpenFile("users.xml", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { log.Fatal(err) } defer file.Close()
data := Person{ Name: "John Doe", Age: 30, City: "New York", }
encoder := xml.NewEncoder(file) err = encoder.Encode(data) if err != nil { log.Fatal(err) }
}
|
上面的代码定义了一个包含三个字段的 Person 结构,以只写和追加模式打开 users.xml 文件,并在文件不存在的情况下创建该文件,延迟关闭函数,并定义了一个包含用户数据的数据变量。然后使用 NewEncoder 函数创建一个编码器变量,并使用 Encoder 函数对其进行编码。<Person><name>John Doe</name><age>30</age><city>New York</city></Person>
|
在 Go 中重命名文件
Go 支持使用重命名功能从代码中重命名文件:
package main
import ( "fmt" "os" )
func main() { err := os.Rename("users.xml", "data.xml") if err != nil { fmt.Println(err) }
}
|
上面的代码将上一节创建的 users.xml 文件重命名为 data.xml。
在 Go 中删除文件
Go 支持使用 Remove 功能删除文件:
package main
import ( "fmt" "os" )
func main() { err := os.Remove("data.bin") if err != nil { fmt.Println(err) } fmt.Println("File deleted") }
|
在 Go 中使用目录
除了文件之外,Go 还提供了可用于在应用程序中执行不同任务的函数。我们将在以下部分中探讨其中一些任务。
创建目录
Go 提供了一个Mkdir可以用来创建空目录的函数:
package main
import ( "fmt" "os" )
func main() { err := os.Mkdir("users", 0755) if err != nil { fmt.Println(err) } fmt.Println("Directory Created Successfully") }
|
上面的代码users在当前工作目录中创建一个文件夹。
在 Go 中创建多个目录
您可以使用以下MkdirAll函数在 Go 中创建多个目录:
package main
import ( "fmt" "os" )
func main() { err := os.MkdirAll("data/json_data", 0755) if err != nil { fmt.Println(err) } fmt.Println("Directory Created Successfully") }
|
上面的代码将创建一个data目录以及json_data其中的一个目录。
注意:如果data目录已经存在,代码只会json_data在其中添加一个目录。
检查 Go 中是否存在目录
为了避免错误,在创建文件或目录之前检查目录是否存在是一个很好的做法。您可以使用Stat函数和IsNotExist函数进行快速检查:
package main
import ( "fmt" "os" )
func main() { if _, err := os.Stat("data/csv_data"); os.IsNotExist(err) { fmt.Println("Directory does not exist") } else { fmt.Println("Directory exists") }
}
|
上面的代码根据检查结果返回一条消息。就我而言,它将返回以下内容:
Directory exists
在 Go 中重命名目录
您还可以使用该Rename函数重命名目录:
package main
import ( "fmt" "os" )
func main() { err := os.Rename("data/csv_data", "data/xml_data") if err != nil { fmt.Println(err) }
}
|
上面的代码将目录重命名data/csv_data为data/xml_data.
Go 中删除空目录
您可以使用该Remove功能删除应用程序中的文件夹:
package main
import ( "fmt" "os" )
func main() { err := os.Remove("data/json_data") if err != nil { fmt.Println(err) }
}
|
上面的代码json_data从data目录中删除该目录。在 Go 中删除目录及其所有内容
Go 提供了一个RemoveAll功能,允许您删除所有目录及其内部的所有内容,包括文件和文件夹:
package main
import ( "fmt" "os" )
func main() { err := os.RemoveAll("users") if err != nil { fmt.Println(err) } fmt.Println("users directory and all it's content has been removed") }
|
上面的代码删除了users目录及其中的所有内容。
注意:在尝试删除该目录之前,最好先检查该目录是否存在。
Go中获取目录中的文件和目录列表
您可以使用以下函数检索目录中所有文件和目录的列表ReadDir:
package main
import ( "fmt" "os" )
func main() { dirEntries, err := os.ReadDir("data") if err != nil { fmt.Println(err) }
for _, entry := range dirEntries { fmt.Println(entry.Name()) } }
|
上面的代码返回文件夹内所有目录和文件的列表data。
现在您已经知道如何在 Go 应用程序中使用目录,让我们在下一节中探讨一些高级文件操作。
Go 中的高级文件操作
在本节中,我们将探讨您在 Go 应用程序中可能遇到的一些高级文件操作。
在 Go 中将压缩数据写入文件
使用压缩文件并不常见,但以下是如何.txt使用包在压缩文件中创建文件的方法compress/gzip:
package main
import ( "compress/gzip" "fmt" "log" "os" )
func main() { file, err := os.OpenFile("data.txt.gz", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { log.Fatal(err) } defer file.Close()
gzipWriter := gzip.NewWriter(file) defer gzipWriter.Close()
data := "Data to compress" _, err = gzipWriter.Write([]byte(data)) if err != nil { log.Fatal(err) } fmt.Println("File compressed successfully") }
|
上面的代码创建了一个data.txt.gz,其中包含data.txt工作目录中的一个文件。
在 Go 中将加密数据写入文件
当构建需要安全文件的应用程序时,您可以使用 Gocrypto/aes和crypto/cipher包创建加密文件:
package main
import ( "crypto/aes" "crypto/cipher" "fmt" "log" "os" )
func main() { // file, err := os.OpenFile("encrypted.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) file, err := os.Create("encrypted.txt") if err != nil { log.Fatal(err) fmt.Println("Error") } defer file.Close()
key := []byte("cacf2ebb8cf3402964356547f20cced5") plaintext := []byte("This is a secret! Don't tell anyone!")
block, err := aes.NewCipher(key) if err != nil { log.Fatal(err) fmt.Println("Error") }
ciphertext := make([]byte, len(plaintext)) stream := cipher.NewCTR(block, make([]byte, aes.BlockSize)) stream.XORKeyStream(ciphertext, plaintext)
_, err = file.Write(ciphertext) if err != nil { log.Fatal(err) fmt.Println("Error") } fmt.Println("Encrypted file created successfully") }
|
上面的代码创建一个encrypted.txt包含该字符串的加密版本的文件plaintext:
?Э_g?L_.?^_?,_?_;?S???{?Lؚ?W4r W?8~?
|
将文件复制到 Go 中的另一个目录
将现有文件复制到不同位置是我们经常做的事情。在 Go 中执行此操作的方法如下:
package main
import ( "fmt" "io" "os" )
func main() { srcFile, err := os.Open("data/json.go") if err != nil { fmt.Println(err) } defer srcFile.Close()
destFile, err := os.Create("./json.go") if err != nil { fmt.Println(err) } defer destFile.Close()
_, err = io.Copy(destFile, srcFile) if err != nil { fmt.Println(err) } fmt.Println("Copy done!") }
|
上面的代码复制目录json.go中的文件data及其内容,然后json.go在根目录中创建另一个具有相同内容的文件。
在 Go 中获取文件属性
Go 允许您使用以下函数获取文件的属性Stat:
package main
import ( "fmt" "os" )
func main() { fileInfo, err := os.Stat("config.json") if err != nil { fmt.Println(err) }
fmt.Println("File name:", fileInfo.Name()) fmt.Println("Size in bytes:", fileInfo.Size()) fmt.Println("Permissions:", fileInfo.Mode()) fmt.Println("Last modified:", fileInfo.ModTime())
fmt.Println("File properties retrieved successfully") }
|
上面的代码返回文件的名称、大小、权限和上次修改日期config.json:
File name: config.json Size in bytes: 237 Permissions: -rw-r--r-- Last modified: 2023-07-11 22:46:59.705875417 +0100 WAT File properties retrieved successfully
|
获取Go中当前工作目录路径
您可以在 Go 中获取应用程序的当前工作目录:
package main
import ( "fmt" "os" )
func main() { wd, err := os.Getwd() if err != nil { fmt.Println(err) }
fmt.Println("Current working directory:", wd)
}
|
上面的代码将返回我当前工作目录的完整路径:
Current working directory: /Users/user12/Documents/gos/go-files