c-如何知道文件是否以换行符结束



我试图在具有以下形状的文件末尾输入一行"1∶1∶1",所以在某个时候,文件的末尾可能会有一个换行符,为了执行操作,我必须处理这个问题,所以我想出了以下解决方案:到文件的末尾,向后退1个字符(我猜是Linux操作系统中换行符的长度),读取该字符,如果不是换行符,插入一个,然后插入整行,否则再插入这行,这就是该解决方案在C:上的翻译

int insert_element(char filename[]){
elements *elem;
FILE *p,*test;
size_t size = 0;
char *buff=NULL;
char c='n';
if((p = fopen(filename,"a"))!=NULL){
if(test = fopen(filename,"a")){
fseek(test,-1,SEEK_END );
c= getc(test);
if(c!='n'){
fprintf(test,"n");
}
}
fclose(test);
p = fopen(filename,"a");
fseek(p,0,SEEK_END);
elem=(elements *)malloc(sizeof(elements));
fflush(stdin);
printf("ninput the IDn");
scanf("%d",&elem->id);
printf("input the adress n");
scanf("%s",elem->adr);
printf("innput the type n");
scanf("%s",elem->type);
printf("intput the mark n");
scanf("%s",elem->mark);
fprintf(p,"%d :%s :%s :%s",elem->id,elem->adr,elem->type,elem->mark);
free(elem);
fflush(stdin);
fclose(p);
return 1;
}else{
printf("nRrror while opening the file !n");
return 0;
}
}

正如你可能注意到的,整个程序取决于换行符的长度(1个字符"\n"),所以我想知道是否有一种最佳的方法,换句话说,适用于所有操作系统的

您似乎已经了解了附加到文件的基本知识,所以我们只需要弄清楚文件是否已经以换行符结束。

在一个完美的世界里,你会跳到文件的末尾,备份一个字符,读取那个字符,看看它是否与'n'匹配。类似这样的东西:

FILE *f = fopen(filename, "r");
fseek(f, -1, SEEK_END);  /* this is a problem */
int c = fgetc(f);
fclose(f);
if (c != 'n') {
/* we need to append a newline before the new content */
}

尽管这可能在Posix系统上有效,但在许多其他系统上都不起作用。这个问题的根源在于系统在文本文件中分隔和/或终止行的许多不同方式。在C和C++中,'n'是一个特殊值,它告诉文本模式输出例程执行插入换行所需的任何操作。同样,文本模式输入例程将在返回读取的数据时将每个换行转换为'n'

在Posix系统(例如Linux)上,换行符由换行符(LF)表示,换行符在UTF-8编码的文本中占据一个字节。因此,编译器只需将'n'定义为换行字符,然后输入和输出例程就不必在文本模式下做任何特殊的事情。

在一些较旧的系统(如旧的MacOS和Amiga)上,换行符可能由回车符(CR)表示。许多IBM大型机使用称为EBCDIC的不同字符编码,它们没有LF或CR的直接映射,但它们有一个称为next line(NL)的特殊控制字符。甚至还有一些系统(如VMS、IIRC)不使用文本文件的流模型,而是使用可变长度的记录来表示每一行,因此换行符本身是隐含的,而不是由特定的控制字符标记。

其中大多数都是现代系统不会面临的挑战。Unicode添加了更多的换行约定,但很少有软件以通用方式支持它们。

剩下的主线中断约定是CR+LF组合。CR+LF之所以具有挑战性,是因为它是两个控制字符,但C i/o函数必须使它们在程序员看来像是单个字符'n'。这对于输入或输出流式传输文本来说并不是什么大不了的事。但这使得在文件中查找变得很难定义。这让我们回到了有问题的路线:

fseek(f, -1, SEEK_END);

"备份"意味着什么;一个字符";从系统的末尾开始,其中换行符由两个字符序列表示,如LF+CR?我们真的希望i/o系统必须扫描整个文件,以便fseek(和ftell)找出如何理解偏移量吗?

C标准的人踢球在文本模式中,fseek的偏移量参数只能是0或上一次调用ftell返回的值。因此,偏移量为负的有问题的调用是无效的。(在Posix系统上,对fseek的无效调用可能会起作用,但标准不要求它起作用。)

还要注意,Posix将LF定义为行终止符,而不是

分隔符对于更便携的解决方案,我们有两种选择:

  1. 在文本模式下读取整个文件,记住最近读取的字符是否为'n'

    这个选项效率非常低,所以除非你只是偶尔或只对短文件这样做,否则我们可以排除这种可能性。

  2. 二进制模式打开文件,从末尾向后查找几个字节,然后读到末尾,记住最后读取的内容是否是有效的换行序列。

    如果在二进制模式下打开文件时,我们的fseek不支持SEEK_END原点,那么这可能是一个问题。是的,C标准规定支持是可选的。但是,大多数实现都支持它,所以我们将保留此选项。

    由于该文件将以二进制模式读取,因此输入例程不会将平台的换行序列转换为'n'。我们需要一个状态机来检测长度超过一个字节的换行序列。

    让我们做一个简化的假设,即换行符是LF或CR+LF。在后一种情况下,我们不关心CR,所以我们可以简单地从末尾备份一个字节,并测试它是否为LF。

    哦,我们必须想办法处理一个空文件。

bool NeedsLineBreak(const char *filename) {
const int LINE_FEED = 'x0A';
FILE *f = fopen(filename, "rb");  /* binary mode */
if (f == NULL) return false;
const bool empty_file = fseek(f, 0, SEEK_END) == 0 && ftell(f) == 0;
const bool result = !empty_file ||
(fseek(f, -1, SEEK_END) == 0 && fgetc(f) == LINE_FEED);
fclose(f);
return result;
}

最新更新