当前位置:   article > 正文

C语言删除TXT文本文件最后一行_c语言删除txt文件最后一行换行符

c语言删除txt文件最后一行换行符

思路分析

目的

希望能将给定txt文件的最后一行(无论是空行还是第一行)删掉,并直接保存,而不是另存为某个新的txt文件。

例1:

原文件:
1234567
abcdefg
处理后:
1234567
  • 1
  • 2
  • 3
  • 4
  • 5

例2:

原文件:
Hello World!

处理后:
Hello World!
  • 1
  • 2
  • 3
  • 4
  • 5

例3:

原文件:
This is an example.
处理后

  • 1
  • 2
  • 3
  • 4

算法流程

大体思路如下:

  1. 以只读方式打开原文件。
  2. 以只写方式打开一个临时文件。
  3. 通过从原文件末尾向前查找换行符的方式确定最后一行的位置
  4. 从原文件开头逐个读取字符,写入临时文件,直到第3步中确定的位置
  5. 关闭文件,保存。
  6. 将原文件删除,将临时文件重命名为原文件的名称。

注:本文只用C语言实现,没有C++的内容。

几个需要注意的点

所用的函数

基本上就这几个:

errno_t __cdecl fopen_s(FILE **_Stream, const char *_FileName, const char *_Mode)
  • 1
int __cdecl fseek(FILE *_Stream, long _Offset, int _Origin)
  • 1
  • fgetc& fputc& fclose& ftell
  • remove& rename

具体用法看后面的源码吧,都不难,这里提供一个C++的帮助文档:cplusplus,大部分时候都挺好用的,所有函数都有例程,还可以在线跑例程。

二进制模式与文本模式

在使用fopen_s打开文件时,可以在第三个参数_Mode中添加字符b来开启二进制模式,否则为文本模式。

例如,fopen_s(&oldFile, txtFilePath, "rb")就是开启了二进制模式。

这两者的区别几乎只在于换行符,在本人的编程环境 Win10 Visual Studio 2019 (v142) 下:

  • 二进制模式读取的换行符为2个字符\r\n;而文本模式为2个字符\n\n
  • 二进制模式写入\n再读取为\n;而文本模式写入\n再读取为\r\n,即文本模式将\n填补为了完整的换行符

可以这么理解:

  • 二进制模式下,写入、读取和实际存储的数据,一定相同
  • 文本模式下,写入、读取和实际存储的数据,不一定相同

fseek的坑

fseek的第三个参数_Origin有以下三个选择:

常量代表的参考位置
SEEK_SET文件开头
SEEK_CUR文件指针的当前位置
SEEK_END文件末尾

(第二个参数_Offset代表相对于参考位置的偏移量,在此不展开赘述)

有趣的事情来了,使用SEEK_SET或者SEEK_END结合适当的偏移量都可以让文件指针换行,但SEEK_CUR无论偏移多少都无法让文件指针换行,十分地不明所以。

源码

Talk is cheap. Show me the code.

#include <stdio.h>
#include <string.h>

int main()
{
	//初始化变量
	FILE* oldFile;
	FILE* newFile;
	char txtFilePath[] = "data.txt";
	char tmpFilePath[] = "data_tmp.txt";

	//用"r"方式打开文件进行读取,"w"方式打开文件进行写入
	//使用二进制模式而不是文本模式打开文件(后面加了"b"),是为了处理起来更准确
	//二进制模式可以保证写入什么字符,就是什么字符,主要是在换行符上有作用
	//二进制模式下如果写入"\n",就是1个字符"\n",而文本模式下会自动填充成2个字符"\r\n"
	if (fopen_s(&oldFile, txtFilePath, "rb") || fopen_s(&newFile, tmpFilePath, "wb")) {
		printf("Error while opening file. \n");
		return 1;
	}

	char buffer = 0; //存储读取的字符
	//从文件末尾开始向前寻找"\n"换行符,并记录位置
	int curPos = 1; //当前光标位置
	while (true) {
		fseek(oldFile, -curPos, SEEK_END); //移动光标到文件倒数第curPos个字符前
		if (ftell(oldFile) == 0) {
			//若没有查找到,且光标已经位于文件开头,说明文件只有一行,则退出循环,删除该行
			break;
		}
		buffer = fgetc(oldFile); //读取字符到buffer
		//printf("%c", buffer); //输出查看
		if (buffer == '\n') {//注:C++中,双引号""表示字符串,单引号''表示字符,字符与字符串不能进行比较运算
			//换行符在二进制模式下为2个字符"\r\n",在文本模式下为2个字符"\n\n"
			//若查找到了,说明光标处于"\r"和"\n"之间,将光标再前移1次
			fseek(oldFile, -curPos - 1, SEEK_END); //将光标定位在倒数第二行的换行符前
			//退出循环,删除该换行符及之后的内容
			break;
		}
		else {
			//若没有查找到,则继续向前查找
			curPos++;
		}
	}
	int endPos = ftell(oldFile); //记录该光标位置

	//将从文件开头到endPos的内容复制到临时文件中
	fseek(oldFile, 0, SEEK_SET); //当前光标回到文件开头
	while (true) {
		curPos = ftell(oldFile);
		if (curPos == endPos) {
			break;
		}
		buffer = fgetc(oldFile); //读取字符到buffer
		fputc(buffer, newFile);
	}

	//关闭文件,保存
	fclose(oldFile);
	fclose(newFile);

	//删除旧文件,将临时文件名称改为旧文件名称,得到新文件
	remove(txtFilePath);
	if (rename(tmpFilePath, txtFilePath)) {
		printf("Error while renaming file. \n");
		return 2;
	}

	//结束
	printf("Successful. \n");
	return 0;
}
  • 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

结语

很久没写C了,其实C++会的也不多……一直都是纯C写得多一点。

C给人的感觉就是一切都尽在掌握,自己会对代码的一举一动了如指掌,以及无比地僵硬(其实我还挺喜欢这种感觉的)。

网上的代码千奇百怪,还是要自己动手,才能写出简洁高效的代码!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/652649
推荐阅读
相关标签
  

闽ICP备14008679号