C语言实现linux下的tail -n <n> 命令

本文介绍了如何用C语言实现Linux系统下的tail -n命令,该命令用于显示文本文件的最后n行。内容包括利用fseek函数从文件尾部开始向前读取,通过计数换行符来确定已读行数。即使文件末尾没有换行符,Linux文本文件也会在最后添加一个额外的换行符。

linux下的tail -n <n>命令的作用就是输出文本文件的最后n行,如果文件的总行数小于n,则输出文件的所用行。


其通过C语言的fseek的实现方法如下:


#include <stdio.h>
#include <stdlib.h>

#define BUFSIZE 256

void myTail(char* filename, unsigned int n)
{
FILE* f = fopen(filename, "r");
if(!f)
{
printf("Can not open the file!\n");
return;
}

fseek(f, -1, SEEK_END); // SEEK_END位置是一个无效的位置,对于文件而言,SEEK_END的前一个位置是文件中最后的那个数据的位置
unsigned int num = 0;
while(true)
{
if(fgetc(f) == '\n') ++num;
if(num > n) break;
if(fseek(f, -2, SEEK_CUR ) != 0) // 当读取好一个字符串后,文件的指针会移动到下一个位置上,所以要倒序读取,此时文件的指针需要向前移动2个字节
{
fseek(f, 0, SEEK_SET); // 如果fseek返回值不是零,说明已经到达的文件的头部,该文件需要从头全部输出
break;
}
}

char buffer[BUFSIZE];
while(fgets(buffer, BUFSIZE, f)) // 此处没有用feof判断,因为用feof判断会导致最后一行输出两次,feof是在文件指针失效后,还要读取一次才判断为真
{
printf("%s", buffer);
}
}


上述函数的思路,就是从文件的结尾开始往前读,读到的换行符的数量 - 1就是已经经过的行数,需要注意的是,对于一个linux下的文本文件,文件的最后一行即使没有换行符在最终文件的最后也是有一个换行符的。如下图,hexdump -C输出文件的二进制中所描述的那样


可以看到,无论文件最后是否换行,都会多出一个最后的换行符。


<think>我们将在Linux下使用C语言实现串口通信,包括以下核心部分: 1. 打开串口设备 2. 配置串口参数(波特率、数据位、停止位、校验位等) 3. 使用环形缓冲区(队列)作为收发数据的缓冲区 4. 创建读取线程,将串口接收到的数据存入接收环形缓冲区 5. 提供发送函数,将数据放入发送环形缓冲区,并实际发送(可以在主循环中发送,也可以使用单独的发送线程) 6. 主程序可以处理接收到的数据并发送数据 参考引用[1][2]中提到的struct termios结构体进行串口配置,引用[3][4]中的环形缓冲区实现。 具体步骤: 步骤1:打开串口设备 步骤2:配置串口参数 步骤3:实现环形缓冲区(接收和发送各一个) 步骤4:创建读取线程,不断读取串口数据并存入接收缓冲区 步骤5:提供发送接口,将数据写入发送缓冲区,并启动发送(可以立即发送或由另一个线程发送) 步骤6:主程序示例 注意:环形缓冲区需要是线程安全的,因为读取线程和主线程可能同时操作接收缓冲区,发送也可能由不同线程操作。 下面我们逐步实现:</think>### Linux下C语言实现带缓冲队列的串口通信 #### 1. **串口设备打开与配置** 使用`termios`结构体配置串口参数[^1][^2]: ```c #include <fcntl.h> #include <termios.h> #include <unistd.h> // 打开串口设备 int serial_open(const char* device) { int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); if (fd < 0) { perror("Error opening serial port"); return -1; } fcntl(fd, F_SETFL, 0); // 恢复阻塞模式 return fd; } // 配置串口参数 int serial_config(int fd, int baud, int databits, char parity, int stopbits) { struct termios options; tcgetattr(fd, &options); // 设置波特率 speed_t speed; switch (baud) { case 9600: speed = B9600; break; case 19200: speed = B19200; break; case 38400: speed = B38400; break; case 115200: speed = B115200; break; default: speed = B9600; } cfsetispeed(&options, speed); cfsetospeed(&options, speed); // 设置数据位 options.c_cflag &= ~CSIZE; switch (databits) { case 5: options.c_cflag |= CS5; break; case 6: options.c_cflag |= CS6; break; case 7: options.c_cflag |= CS7; break; case 8: default: options.c_cflag |= CS8; } // 设置校验位 switch (parity) { case 'O': // 奇校验 options.c_cflag |= PARENB; options.c_cflag |= PARODD; break; case 'E': // 偶校验 options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; break; case 'N': // 无校验 default: options.c_cflag &= ~PARENB; } // 设置停止位 options.c_cflag &= ~CSTOPB; if (stopbits == 2) options.c_cflag |= CSTOPB; // 基本配置 options.c_cflag |= (CLOCAL | CREAD); // 本地连接,启用接收 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始输入模式 options.c_oflag &= ~OPOST; // 原始输出模式 options.c_cc[VMIN] = 0; // 最小读取字符数 options.c_cc[VTIME] = 10; // 读取超时(1秒) tcsetattr(fd, TCSANOW, &options); return 0; } ``` #### 2. **环形缓冲区实现** 参考引用[3][^3]实现线程安全的环形缓冲区: ```c #include <pthread.h> #define BUFFER_SIZE 1024 typedef struct { unsigned char buffer[BUFFER_SIZE]; int head; int tail; pthread_mutex_t lock; } RingBuffer; // 初始化缓冲区 void ringbuf_init(RingBuffer* rb) { rb->head = 0; rb->tail = 0; pthread_mutex_init(&rb->lock, NULL); } // 写入数据 int ringbuf_write(RingBuffer* rb, unsigned char data) { pthread_mutex_lock(&rb->lock); int next = (rb->head + 1) % BUFFER_SIZE; if (next == rb->tail) { // 缓冲区满 pthread_mutex_unlock(&rb->lock); return -1; } rb->buffer[rb->head] = data; rb->head = next; pthread_mutex_unlock(&rb->lock); return 0; } // 读取数据 int ringbuf_read(RingBuffer* rb, unsigned char* data) { pthread_mutex_lock(&rb->lock); if (rb->tail == rb->head) { // 缓冲区空 pthread_mutex_unlock(&rb->lock); return -1; } *data = rb->buffer[rb->tail]; rb->tail = (rb->tail + 1) % BUFFER_SIZE; pthread_mutex_unlock(&rb->lock); return 0; } // 获取缓冲区数据量 int ringbuf_count(RingBuffer* rb) { pthread_mutex_lock(&rb->lock); int count = (rb->head >= rb->tail) ? (rb->head - rb->tail) : (BUFFER_SIZE - rb->tail + rb->head); pthread_mutex_unlock(&rb->lock); return count; } ``` #### 3. **数据收发线程** ```c #include <pthread.h> RingBuffer rx_buf, tx_buf; // 接收线程函数 void* receive_thread(void* arg) { int fd = *((int*)arg); unsigned char ch; while (1) { int n = read(fd, &ch, 1); if (n > 0) { ringbuf_write(&rx_buf, ch); } usleep(10000); // 10ms延迟避免CPU过高 } return NULL; } // 发送线程函数 void* transmit_thread(void* arg) { int fd = *((int*)arg); unsigned char ch; while (1) { if (ringbuf_read(&tx_buf, &ch) == 0) { write(fd, &ch, 1); } else { usleep(5000); // 缓冲区空时短暂休眠 } } return NULL; } // 发送数据接口 void serial_send(int fd, const unsigned char* data, int len) { for (int i = 0; i < len; i++) { while (ringbuf_write(&tx_buf, data[i]) != 0) { usleep(1000); // 等待缓冲区空间 } } } ``` #### 4. **完整使用示例** ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> int main() { // 初始化缓冲区 ringbuf_init(&rx_buf); ringbuf_init(&tx_buf); // 打开并配置串口 int fd = serial_open("/dev/ttyUSB0"); if (fd < 0) return EXIT_FAILURE; // 配置: 115200波特率, 8数据位, 无校验, 1停止位 serial_config(fd, 115200, 8, 'N', 1); // 创建收发线程 pthread_t rx_tid, tx_tid; pthread_create(&rx_tid, NULL, receive_thread, &fd); pthread_create(&tx_tid, NULL, transmit_thread, &fd); // 主循环处理接收数据 unsigned char data; while (1) { int count = ringbuf_count(&rx_buf); if (count > 0) { printf("Received %d bytes: ", count); while (ringbuf_read(&rx_buf, &data) == 0) { printf("%02X ", data); // 十六进制显示 } printf("\n"); } // 示例: 每秒发送测试数据 static int counter = 0; unsigned char test_data[] = {0xAA, 0x55, counter++ % 256}; serial_send(fd, test_data, sizeof(test_data)); sleep(1); } close(fd); return EXIT_SUCCESS; } ``` #### 5. **关键配置参数对照表** | 参数 | 函数参数 | 可选值 | |--------------|----------------------|--------------------------------| | **波特率** | `baud` | 9600, 19200, 38400, 115200 | | **数据位** | `databits` | 5, 6, 7, 8 | | **校验位** | `parity` | 'N'(无), 'O'(奇), 'E'(偶) | | **停止位** | `stopbits` | 1, 2 | | **流控** | 需扩展 | 0=无, 1=RTS/CTS, 2=XON/XOFF | > **编译命令**:`gcc serial.c -o serial -lpthread` #### 6. **应用场景** 1. **工业设备监控**:实时采集传感器数据 2. **嵌入式开发**:与微控制器通信调试 3. **物联网网关**:连接LoRa/NB-IoT模块 4. **机器人控制**:发送运动指令和接收状态反馈
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值