linux系统的文件接口是十分重要的抽象, 也就是“一切都是文件”。借助文件IO,用户程序可以读写磁盘设备、网络设备、甚至管道、内存等。

C语言

首先介绍linux 系统调用提供的文件io

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 打开文件, 返回文件描述符。文件描述符是进程持有打开文件指针序列的索引, 通过文件描述符进程可以访问进程文件结构, 进程文件结构记录文件的Inode,以及进程当前读写文件的偏移等信息
int fd = open("file.txt", O_RDONLY);

size_t bytes = read(fd, buf, sizeof(buf)) // 从文件描述符读取数据到buf

size_t bytes = write(fd, buf, sizeof(buf)) // 将buf写入到文件描述符

close(fd);

off_t offset = lseek(fd, 0, SEEK_END); // 调整文件读写偏移量

// 内存映射文件
#include <sys/mman.h>
int fd = open("data.bin", O_RDWR);
void* addr = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// 直接操作 addr 指针读写文件
munmap(addr, file_size);

C语言的文件IO位于stdio.h库,stdio默认启用用户态缓冲区(可通过 setvbuf 调整),可以减少系统调用次数,提升小文件效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
typedef struct _IO_FILE FILE;
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
};

FILE *fopen(const char *filename, const char *mode); // 打开文件, 获得文件句柄
// mode·
// "r":以只读方式打开文件,文件必须存在。
// "w":以只写方式打开文件,如果文件已存在,会清空文件内容;如果文件不存在,会创建新文件。
// "a":以追加模式打开文件,数据写入到文件末尾,文件不存在时会创建。
// "r+":以读写模式打开文件,文件必须存在。
// "w+":以读写模式打开文件,文件不存在时会创建,已存在时会清空文件内容。
// "a+":以读写模式打开文件,数据写入文件末尾,文件不存在时会创建。

int fclose(FILE *stream); // 关闭文件

int fgetc(FILE *stream) // 读取文件, 从文件中读取一个字符。

int fgets(char *str, int num, FILE *stream) // 从文件中读取一行,最多读取 num-1 个字符。
// 从文件中读取多个ptr对象(如结构体、数组等),每个对象大小为 size,读取 count 个对象。
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
int numbers[5];
size_t n = fread(numbers, sizeof(int), 5, file);

int fputc(int c, FILE *stream) // 将字符 c 写入文件流

int fputs(const char *str, FILE *stream) // 将字符串 str 写入文件,自动加上字符串的结束符 \0
// 写入多个对象到文件,每个对象大小为 size,写入 count 个对象。
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
int numbers[5] = {1, 2, 3, 4, 5};
size_t n = fwrite(numbers, sizeof(int), 5, file);

// 文件定位
// 设置文件指针的位置。offset 是偏移量,whence 是参考位置。
// SEEK_SET:从文件开头开始偏移。
// SEEK_CUR:从当前位置开始偏移
// SEEK_END:从文件末尾开始偏移
fseek(FILE *stream, long offset, int whence):

ftell(FILE *stream) // 获取当前文件指针的位置

int feof(FILE *stream); // 检查文件是否已读到末尾

// 标准输入输出
// %d:输出10进制整数; %f:输出浮点数; %s:输出字符串; %c:输出字符;%x:输出十六进制整数。
int printf(const char *format, ...);
int scanf(const char *format, ...);
scanf("%d", &a);

getchar() // 从标准输入读取一个字符。
putchar() // 将一个字符输出到标准输出

int fscanf(FILE *stream, const char *format, ...); // 从文件中按指定的格式读取数据,并将读取的数据存储到变量。

int fprintf(FILE *stream, const char *format, ...); // fprintf() 向文件中写入格式化的数据

// sprintf() 将格式化的数据写入字符串。
int sprintf(char *str, const char *format, ...);
sprintf(buffer, "Number: %d, Pi: %.2f", num, pi);

// sscanf() 函数用于从字符串中按指定格式读取数据
int sscanf(const char *str, const char *format, ...);

linu网络socket系统调用

  1. 服务端,绑定(bind)到指定端口。监听(listen)端口,等待客户端连接。接受(accept)客户端的连接请求。进行数据通信(send,recv, read, write),关闭 Socket(close)。
  2. 客户端,创建 Socket。连接(connect)到服务器端。发送和接收数据。关闭 Socket。

三次握手之前, 服务端和客户端分别阻塞在accept和connect,三次握手建立连接后, 向下执行。三次握手过程

  1. 客户端发送SYN报文(SYN=1,序列号seq=x), 客户端进入 SYN_SENT 状态
  2. 服务器回复SYN-ACK报文(SYN=1,ACK=1,确认号ack=x+1,序列号seq=y), 服务器进入 SYN_RCVD 状态。客户端请求加入到SYN队列, 由net.ipv4.tcp_max_syn_backlog控制
  3. 客户端发送ACK报文(ACK=1,确认号ack=y+1,序列号seq=x+1)。客户端进入 ESTABLISHED 状态。服务器收到后也进入 ESTABLISHED 状态。请求进入ESTABLISHED队列,大小由net.core.somaxconn控制。
    使用三次握手, 服务端可以控制连接的流控,例如服务端能力不足时,可以通过减缓发送SYN-ACK的频率,减少连接创建。同时双方都可以确定对方接受和发送连接正常。

close()函数, 释放套接字文件描述符(File Descriptor),减少其引用计数。当引用计数归零时,触发 TCP 连接的关闭流程(发送 FIN 包,进入四次挥手阶段),主动关闭双向连接。TCP四次回收关闭

  1. 主动方发送FIN报文(FIN=1,序列号seq=u), 主动方从 ESTABLISHED 进入 FIN_WAIT_1 状态。此时不再发送数据,但可接收数据
  2. 被动方回复ACK报文(ACK=1,确认号ack=u+1,序列号seq=v), 被动方进入 CLOSE_WAIT 状态,主动方收到后进入 FIN_WAIT_2 状态。此时被动方还可以发送数据
  3. 被动方数据发送完毕后,发送FIN报文(FIN=1,ACK=1,确认号ack=u+1,序列号seq=w),被动方进入 LAST_ACK 状态。
  4. 主动方发送ACK报文(ACK=1,确认号ack=w+1,序列号seq=u+1), 主动方进入 TIME_WAIT 状态,等待 ​​2MSL​​(最长报文段寿命,MSL在 /proc/sys/net/ipv4/tcp_fin_timeout配置, 默认60s)后关闭。被动方收到后进入 CLOSED 状态。

shutdown(),主动关闭套接字的 ​​单向或双向​​ 通信通道,立即触发 TCP 协议栈的关闭流程, 不影响文件描述符。shutdown支持允许半关闭,即只关闭一端网络

  1. shutdown(SHUT_WR), 关闭发送端, 触发两次挥手。主动方发送 FIN → 进入 FIN_WAIT_1, 被动方回复 ACK → 主动方进入 FIN_WAIT_2,被动方进入 CLOSE_WAIT。被动方需调用 close() 或 shutdown() 关闭连接,才会发送 FIN,触发完整四次挥手。若被动方不关闭,主动方会停留在 FIN_WAIT_2,直到内核超时(默认约 60 秒)
  2. shutdown(SHUT_RD),关闭接收端, 丢弃接收缓冲区的数据,不再读取新数据。​不发送任何报文​​,因此不会触发挥手流程。对方可能继续发送数据,但会被本地内核丢弃(无 RST 或 ACK 回应)。
  3. shutdown(SHUT_RDWR), 触发完整四次挥手。

服务端大量TIME_WAIT链接,在高并发短连接的TCP服务器上,服务器处理完请求后往往选择立刻主动正常关闭连接。或者当 connection 头部取值被设置为 close 时,基本都由「服务端」发起主动关闭连接。

  1. 处理办法,在HTTP 请求的头部,connection 设置为 keep-alive,保持存活一段时间:现在的浏览器,一般都这么设置
  2. 缩短timewait时间为1MSL
  3. 如果是客户端主动关闭导致的TIMEWAIT(例如爬虫程序访问完请求立刻关闭连接),那可能导致客户端端口耗尽,可以开启net.ipv4.tcp_tw_reuse允许客户端新建连接复用timewait的链接。通过net.ipv4.ip_local_port_range扩大客户端使用端口的范围。
1
2
3
4
5
6
7
#include <sys/socket.h>
int shutdown(int sockfd, int how);

// how的取值
// SHUT_RD 关闭接收端,丢弃未读数据。
// SHUT_WR 关闭发送端,发送 FIN 包。
// SHUT_RDWR 同时关闭读和写。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 创建socket
// domain:指定协议族,常用的有:
// AF_INET:IPv4协议。
// AF_INET6:IPv6协议。
// AF_UNIX:Unix 域协议,用于同一机器上的进程间通信。
//
// type:指定套接字类型,常见的有:
// SOCK_STREAM:面向连接的流式套接字,通常用于 TCP。
// SOCK_DGRAM:数据报套接字,通常用于 UDP。
//
// protocol:指定协议,通常为 0,表示由操作系统自动选择合适的协议。
int socket(int domain, int type, int protocol);

// bind() 用于将套接字与本地IP 地址和端口号绑定。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // 绑定到任意IP地址
server_addr.sin_port = htons(PORT); // 设置端口号
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}

// backlog:等待连接队列的最大长度,表示操作系统在拒绝新连接之前,最多允许排队的连接请求数。
int listen(int sockfd, int backlog);

// accept() 用于接受一个传入的连接请求。该函数会阻塞,直到有客户端连接。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

// connect() 用于客户端请求与服务器建立连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

// 从socket fd获得数据
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t read(int fd, void *buf, size_t count);
// 向socket fd发送数据
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t write(int fd, const void *buf, size_t count);

// 关闭一个打开的套接字
int close(int fd);
// shutdown() 可用于关闭连接的一部分(如读或写)
// how:关闭方式,可能的值有:SHUT_RD:关闭读取。SHUT_WR:关闭写入。SHUT_RDWR:关闭读取和写入。
int shutdown(int sockfd, int how);

C++

C++ 文件IO库, 主要是

  1. ifstream:用于从文件中读取数据。
  2. ofstream:用于向文件中写入数据。
  3. fstream:用于同时从文件中读取和写入数据。
  4. iostream:用于输入输出流。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    // 可用文件流类的构造函数或者 open() 方法来打开文件。mode可以选择
    // ios::in:以读取模式打开文件(默认)。
    // ios::out:以写入模式打开文件。
    // ios::app:以追加模式打开文件(写入内容会被追加到文件末尾)。
    // ios::binary:以二进制模式打开文件。
    // ios::trunc:如果文件已经存在,截断文件为零长度
    std::ofstream outfile("example.txt", std::ios::out | std::ios::trunc);

    // 检查文件流状态
    is_open() // 检查文件是否成功打开。
    eof() // 检查是否已经到达文件末尾。

    // 文件指针定位
    seekg(offset, direction) // 设置读取位置。
    seekp(offset, direction) // 设置写入位置。
    tellg() // 返回当前读取位置。
    tellp() // 返回当前写入位置

    #include <iostream>
    #include <fstream>
    #include <string>

    int main() {
    std::ifstream infile("example.txt");

    if (!infile) {
    std::cerr << "Failed to open file.\n";
    return 1;
    }

    std::string line;
    while (std::getline(infile, line)) {
    std::cout << line << std::endl;
    }

    infile.close();
    return 0;
    }

    std::ofstream fout("data.txt");
    fout << 123 << " " << 3.14; // 涉及类型转换和格式化

C++ 的 I/O 流(如 << 和 >> 运算符)需要处理数据类型转换、格式化和错误检查,导致多次虚函数调用和条件判断。每次 I/O 操作会检查流的状态(如 fail()、eof()),增加分支判断。一般要避免使用(包括cout和cin)

优化C++流的方案

  1. 使用二进制模式​, 避免文本解析开销。std::ofstream fout(“data.bin”, std::ios::binary);
  2. 关闭与 C 标准库的同步,提升流操作速度。std::ios::sync_with_stdio(false); // 关闭与 stdio 的同步

不建议使用C++的流,除了C语言的stdio,C++常用的流还有protobuf。protobuf 支持ZeroCopy流

1
2
3
4
5
6
7
8
9
10
#include <google/protobuf/io/zero_copy_stream_impl.h>

// 创建 ZeroCopyOutputStream(如写入文件)
int fd = open("data.bin", O_WRONLY);
google::protobuf::io::FileOutputStream file_stream(fd);

// 直接序列化到文件流,无中间拷贝
MyMessage msg;
msg.set_bytes_data("large_data"); // 假设是大块数据
msg.SerializeToZeroCopyStream(&file_stream);

自定义高效流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class SharedMemoryInputStream : public google::protobuf::io::ZeroCopyInputStream {
public:
SharedMemoryInputStream(void* shared_mem, int size)
: ptr_(static_cast<const uint8_t*>(shared_mem)), size_(size), position_(0) {}

bool Next(const void** data, int* size) override {
if (position_ >= size_) return false;
*data = ptr_ + position_;
*size = size_ - position_;
position_ = size_;
return true;
}

void BackUp(int count) override { position_ -= count; }
bool Skip(int count) override { position_ += count; return position_ <= size_; }
int64 ByteCount() const override { return position_; }

private:
const uint8_t* ptr_;
int size_;
int position_;
};

// 使用自定义流解析数据
void* shared_mem = ...; // 共享内存地址
int mem_size = ...;
SharedMemoryInputStream stream(shared_mem, mem_size);
MyMessage msg;
msg.ParseFromZeroCopyStream(&stream);

JAVA

文件IO

JAVA文件流分为两种主要类型:字节流和字符流。

  1. 字节流用于处理所有二进制文件
  2. 字符流则专门用于处理字符/文本文件

字节流FileInputStream

1
2
3
4
5
6
7
8
9
// 复制图片(字节流)
try (InputStream in = new FileInputStream("input.jpg");
OutputStream out = new FileOutputStream("output.jpg")) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}

字符流FileReader,会自动将字节按编码转换为字符

1
2
3
4
5
6
7
8
// 读取文本文件(字符流)
try (Reader reader = new FileReader("input.txt", StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(reader)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}

缓冲流, 提供了一个缓冲区,可以减少对硬盘的读取次数,从而提高性能。

RandomAccessFile,支持随机访问文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.io.RandomAccessFile;
import java.io.IOException;

public class RandomAccessFileExample {
public static void main(String[] args) {
try (RandomAccessFile file = new RandomAccessFile("example.txt", "rw")) {
file.writeUTF("Hello, World!"); // 写入字符串
file.seek(0); // 将文件指针移动到文件开头
String data = file.readUTF(); // 读取字符串
System.out.println("Read from file: " + data);
} catch (IOException e) {
e.printStackTrace();
}
}
}

标准输入输出

System.out 是 Java 中标准输出流,是 PrintStream 类型的对象

System.in 是 Java 中标准输入流, 是 InputStream 类型的对象,读取的是byte数据。

对于字符数据,通常会使用 Scanner 或 BufferedReader 类接收输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Scanner;

public class ScannerInput {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter your name:");
String name = scanner.nextLine(); // 读取一行字符串
System.out.println("Enter your age:");
int age = scanner.nextInt();
System.out.println("Name: " + name + ", Age: " + age);
scanner.close();
}
}
// 格式化输出
System.out.printf(String format, Object... args);

网络库

JAVA NIO(New Input/Output)是Java提供的高性能、非阻塞I/O框架,位于java.nio 包, 适用于高并发网络编程。

  1. Buffer(缓冲区)​​临时存储数据,所有读写操作均通过缓冲区完成。
  2. Channel(通道)​​连接数据源与缓冲区,支持双向读写。包括FileChannel:文件I/O, ServerSocketChannel:TCP服务端
  3. Selector(选择器)​​单线程监听多个通道事件(如连接、读、写)。

JAVA Netty​​ 是一个高性能、异步事件驱动的网络应用框架, 基于 Java NIO(Non-blocking I/O)设计

  1. 基于 Reactor 模型,通过多路复用(Selector)实现非阻塞 I/O。
  2. 内置 HTTP/1.x、HTTP/2、WebSocket、TCP/UDP、gRPC 等协议支持。
  3. 零拷贝和内存优化

核心抽象

  1. Channel​​ 网络连接的抽象,代表一个开放的 Socket 连接。
  2. ​​EventLoop​​:事件循环,处理 I/O 操作和事件(如连接、读写)。​​EventLoopGroup​​:管理一组 EventLoop,通常分为 BossGroup(接收连接)和 WorkerGroup(处理 I/O)。
  3. ​​Pipeline​​:处理链,由多个 ChannelHandler 组成,负责处理入站(Inbound)和出站(Outbound)事件。
  4. ​Handler​​:业务逻辑单元,如编解码、日志、业务处理。
  5. ByteBuf, Netty 的字节容器,支持堆内/堆外内存、内存池化。

Go

文件IO

golang中,io包定义了 I/O 操作的抽象接口(Reader、Writer、io.Closer)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
// 随机访问
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}

type ReadWriter interface {
Reader
Writer
}

文件 I/O 操作主要通过 os、io 和 bufio 包实现

  1. os包,直接与操作系统交互,提供最底层的文件描述符(*os.File)操作。方法Open、Create、Close、Stat、Chmod、Truncate等。每次读写都是系统调用。
  2. bufio包,是io包接口的一种实现,提供了缓冲区,可以减少系统调用次数,提高性能。缓冲读取(bufio.Reader、Scanner)。缓冲写入(bufio.Writer)。

实现io包的抽象接口

  1. os.File, 直接与操作系统交互,提供最底层的文件描述符(*os.File)操作。方法Open、Create、Close、Stat、Chmod、Truncate等。每次读写都是系统调用。
  2. net.Conn, 提供了网络IO的Read(b []byte) (n int, err error)等接口, net.TCPConn、net.UDPConn 等具体实现
  3. bufio,提供了缓冲io,一般需要os.File或net.Conn的构造

tcp实现的Read, Write接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/net/net.go
func (c *conn) Read(b []byte) (int, error) {
// 调用 netFD(网络文件描述符)的 Read 方法
n, err := c.fd.Read(b)
return n, err
}

// src/internal/poll/fd_unix.go
func (fd *FD) Read(p []byte) (int, error) {
for {
n, err := syscall.Read(fd.Sysfd, p)
if err != nil {
// 处理 EAGAIN(非阻塞模式下无数据)
}
return n, err
}
}

os.File和bufio

1
2
3
4
5
6
7
8
// 用file构造bufio.Reader,形成带缓冲的读取
file, _ := os.Open("data.txt")
reader := bufio.NewReader(file) // 默认 4096 字节缓冲
// 或自定义缓冲区大小
reader = bufio.NewReaderSize(file, 8192) // 8KB 缓冲
// 用file构造bufio.Writer,形成带缓冲的写入
file, _ := os.Create("output.txt")
writer := bufio.NewWriter(file)

打开文件, os.Open, os.OpenFile

1
2
3
4
5
6
7
8
9
10
file, err := os.Open("data.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close() // 确保文件关闭

file, err := os.Create("output.txt")
defer file.Close()

file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_WRONLY, 0644)

读取文件, os.ReadFile, bufio.NewScanner(file), file.Read(buffer)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// os.ReadFile读取全部内容
data, err := os.ReadFile("data.txt") // Go 1.16+
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))


// 利用bufio 包装file逐行读取
file, _ := os.Open("data.txt")
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fmt.Println(line)
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}

// 利用file.Read分块读取
file, _ := os.Open("data.txt")
buffer := make([]byte, 4096)
for {
n, err := file.Read(buffer)
if err == io.EOF {
break
}
process(buffer[:n])
}

写入文件os.WriteFile, file.WriteString, writer.WriteString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 写入字节到文件
data := []byte("Hello, Go!\n")
err := os.WriteFile("output.txt", data, 0644)

// 追加写入字符串到文件
file, _ := os.OpenFile("log.txt", os.O_APPEND|os.O_WRONLY, 0644)
defer file.Close()

_, err := file.WriteString("New log entry\n")

// 缓冲写入
writer := bufio.NewWriter(file)
_, err := writer.WriteString("Buffered data\n")
writer.Flush() // 确保数据写入磁盘

序列化数据到文件

1
2
3
4
5
6
7
8
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}

user := User{"Alice", 30}
data, _ := json.Marshal(user)
os.WriteFile("user.json", data, 0644)

golang fmt格式化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 参数
%v 值的默认格式表示
%+v 带字段名的结构体表示

%c 对应 Unicode 码点的字符
%d 十进制表示
%x 十六进制(小写字母)
%X 十六进制(大写字母)

%s 字符串
%.nf: 指定小数点后 n 位
%m.nf 设置宽度和小数位数


fmt.Printf("My name is %s and I am %d years old.\n", name, age) // 格式化输出
fmt.Fprint(os.Stdout, "Hello, world!") // 将格式化的输出写入到指定的 io.Writer
fmt.Fprintf(file, "Hello, file!")

fmt.Scanf("%s %d", &name, &age) // fmt.Scanf() 格式化输入
fmt.Scanln(&name)

result := fmt.Sprintf("Name: %s, Age: %d", name, age) // 字符串格式化,返回新字符串
fmt.Sscanf(input, "%s %d", &name, &age)

网络库

Go 语言的 net 包是标准库中用于网络编程的核心模块,提供了简洁高效的 API 支持 TCP/IP、UDP、HTTP 等协议。net/http和net/rpc分别支持http和rpc协议。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import "net"
// 客户端
func main() {
// 连接到 TCP 服务器
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("Error dialing:", err)
os.Exit(1)
}
defer conn.Close()

// 向服务器发送数据
_, err = conn.Write([]byte("Hello, Server!"))
if err != nil {
fmt.Println("Error sending message:", err)
return
}

// 读取服务器响应
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("Error reading response:", err)
return
}

// 打印接收到的响应
fmt.Printf("Received: %s\n", string(buffer[:n]))
}

// 服务端
func main() {
// 在本地启动一个 TCP 服务器,监听端口 8080
listen, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Error starting server:", err)
os.Exit(1)
}
defer listen.Close()

fmt.Println("Server started, waiting for connections...")

// 接受客户端连接
for {
conn, err := listen.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
continue
}
defer conn.Close()

// 读取客户端消息
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("Error reading from connection:", err)
}

// 输出接收到的消息
fmt.Printf("Received: %s\n", string(buffer[:n]))

// 向客户端发送响应
_, err = conn.Write([]byte("Hello, Client!"))
if err != nil {
fmt.Println("Error sending response:", err)
}
}
}

flag 命令行解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
// 定义命令行参数
var name string
var age int
var isAdmin bool

// 通过 flag 包解析命令行标志
flag.StringVar(&name, "name", "Guest", "Your name")
flag.IntVar(&age, "age", 18, "Your age")
flag.BoolVar(&isAdmin, "admin", false, "Is admin?")

flag.Parse()

fmt.Printf("Name: %s\n", name)
fmt.Printf("Age: %d\n", age)
fmt.Printf("Is Admin: %v\n", isAdmin)
}

// 执行, go run main.go -name=John -age=30 -admin=true

Python

文件IO

python 提供open, file, read, write, close 等原生操作文件的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 打开文件
# 'r':只读模式,文件必须存在。
# 'w':写入模式,若文件已存在则覆盖,不存在则创建。
# 'a':追加模式,在文件末尾追加内容,若文件不存在,则创建。
# 'b':二进制模式(如读取或写入图片、音频等)
file = open('filename', mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

with open('example.txt', 'r') as file:
content = file.read() # 读取全部内容
print(content)

file.seek(0) # 文件指针移动到开头
first_10_chars = file.read(10) # 读取前10个字符
print(first_10_chars)

print(file.tell()) # 输出文件指针当前位置

file.write("Hello, World!\n") # 写入内容

lines = ['Line 1\n', 'Line 2\n', 'Line 3\n']
file.writelines(lines) # 写入多行
file.flush() # 立即写入数据

python的os 模块提供文件元数据操做的方法,如remove, stat, rename, remove, mkdir

1
2
3
4
5
os.remove('example.txt')  # 删除文件
# 获取文件状态信息
stat_info = os.stat('example.txt')
# 创建目录
os.mkdir('new_directory')

print() 函数用于标准输出, 支持格式化输出

1
2
3
4
name = "Alice"
age = 25
print(f"My name is {name}, and I am {age} years old.")
print("My name is {}, and I am {} years old.".format(name, age))

input()用于标准输入, input读到的类型都是字符串, 需要转换为对应类型

1
2
3
4
name = input("Enter your name: ")
print(f"Hello, {name}!")

age = int(input("Enter your age: "))

argparse 命令行参数解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import argparse

# 创建 ArgumentParser 对象
parser = argparse.ArgumentParser(description="A simple command-line tool")

# 添加参数
parser.add_argument('name', type=str, help="Your name")
parser.add_argument('age', type=int, help="Your age")
parser.add_argument('--admin', action='store_true', help="Are you an admin?")

# 解析命令行参数
args = parser.parse_args()

# 使用参数
print(f"Name: {args.name}")
print(f"Age: {args.age}")
print(f"Admin: {args.admin}")

json 序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import json
data = {
"name": "Alice",
"age": 25,
"city": "New York"
}

# 打开文件并写入 JSON 数据
with open("data.json", "w") as json_file:
json.dump(data, json_file, indent=4)
# 从文件中读取 JSON 数据
with open("data.json", "r") as json_file:
data = json.load(json_file)
# 对象编码
json_string = json.dumps(data, indent=4)
# 对象解码
data = json.loads(json_string)