我正在开发一个 C 程序,该程序将使用 Linux 系统调用打开、读取和关闭文件,并将文件的内容打印到屏幕上。命令格式为
$ mycat [-bens] f1 [f2 ...].
开关如下:
-b
显示每个非空行的行号,从 1 开始-e
在每行末尾显示“$”-n
显示每行的行号-s
从输出中删除所有空行(有效地对输出进行单倍行距)
问题是,当我使用 -b
或 -n
开关时,printf
似乎与行号“重叠”缓冲区试图从文本文件本身打印什么。
这是我为该程序编写的代码:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#define BUFFERSIZE 4096
void oops(char *, char *);
int main(int ac, char *av[])
{
int fd, numRead, curr, i, c;
char buf[BUFFERSIZE] = {0};
extern char *optarg;
extern int optind;
int tmpS = 0;
int tmpB = 0;
int bFlag = 0;
int eFlag = 0;
int nFlag = 0;
int sFlag = 0;
int bLineNum = 1;
int nLineNum = 1;
/* Flag processing in argument list */
while( (c = getopt(ac, av, "bens")) != -1)
{
switch(c)
{
case 'b':
bFlag = 1;
break;
case 'e':
eFlag = 1;
break;
case 'n':
nFlag = 1;
break;
case 's':
sFlag = 1;
break;
default:
exit(EXIT_FAILURE);
}
}
/* Scan through each argument after flag */
for(i = optind; i < ac; i++)
{
/* Error handling when opening each file */
if((fd = open(av[i], O_RDONLY)) == -1)
oops("Cannot open ", av[i]);
/* Read from file to buffer, until end is reached */
while( (numRead = read(fd, buf, BUFFERSIZE)) > 0)
{
/* Once buffer is filled, process each address in buffer */
for(curr = 0; curr < BUFFERSIZE; curr++)
{
/* sFlag squeezes output, eliminating blank lines */
if(sFlag && buf[curr] == '\n')
{
tmpS = curr + 1;
while(buf[tmpS] != '\r')
{
if(isspace(buf[tmpS]))
tmpS++;
else
break;
}
curr = tmpS + 1;
}
/* nFlag numbers each line, starting from 1 */
if(nFlag && buf[curr] == '\n')
printf("%d ", nLineNum++);
/* eFlag puts a '$' at the end of every line */
if(eFlag && buf[curr] == '\r')
printf(" $");
/* bFlag numbers every non-blank line, starting from 1 */
if(bFlag && buf[curr] == '\n')
{
tmpB = curr + 1;
if(isEmptyLine(buf, tmpB))
printf("%d ", bLineNum++);
}
/* Print the current character in the buffer address */
printf("%c", buf[curr]);
}
}
if(numRead == -1)
oops("Read error from ", av[i]);
}
return 0;
}
void oops(char *s1, char *s2)
{
fprintf(stderr, "Error: %s ", s1);
perror(s2);
exit(1);
}
int isEmptyLine(char *buf, int tmp)
{
while(buf[tmp] != '\n')
{
if(!isspace(buf[tmp]))
return 0;
tmp++;
}
return 1;
}
示例输入(file1.txt):
An excerpt from LEARNING DOS FOR THE COMPLETE NOVICE, by Steven Woas, copyright 1993.
1. Change to the compressed drive and then issue a CHKDSK
command like so:
c: <ENTER>
chkdsk /f <ENTER>
The /F tells DOS to fix errors.
Another option is to do it like so:
dblspace /chkdsk /f <ENTER>
A shortcut for the DBLSPACE /CHKDSK /F command is:
dblspace /chk /f <ENTER>
打开并运行 -n 标志的输出:
sh-4.2$ ./main -n file1.txt
1 excerpt from LEARNING DOS FOR THE COMPLETE NOVICE, by Steven Woas, copyright 1993.
2
3 Change to the compressed drive and then issue a CHKDSK
4 command like so:
5
6 c: <ENTER>
7
8 chkdsk /f <ENTER>
9
10 The /F tells DOS to fix errors.
11
12 Another option is to do it like so:
13
14 dblspace /chkdsk /f <ENTER>
15
16 A shortcut for the DBLSPACE /CHKDSK /F command is:
17
18 dblspace /chk /f <ENTER>
我在使用 -b
标志时遇到了同样的问题,但我不知道为什么。这是否与 \r
和 \n
未正确读取有关?
最佳答案
您的程序表现出您所描述的针对具有 Windows-(DOS-) 样式行结尾 ( \r\n
) 的文件的错误行为,但对于具有 UNIX 样式行结尾的文件 (单独 \n
) 的错误行为,以及不同的错误行为具有 MacOS 类样式行结尾的文件(仅 \r
)。由于您似乎总体上假设 Windows 样式的行结尾,因此我将重点关注这一点。
考虑当程序到达行尾时会发生什么。它首先处理 \r
字符,最终打印它。这会导致输出位置返回到行的开头(这是可能的,因为标准输出默认情况下是行缓冲的)。然后打印行号,覆盖之前可能存在的任何内容,最后打印 \n
字符,导致缓冲区被刷新并且输出移动到下一行。
您可能应该认识 \r\n
序列作为行结尾,而不是尝试单独处理这些字符。这可能有点具有挑战性,因为您需要考虑该货币对分为两个 read()
的可能性。 s,但这应该不会太难。这也让你有机会考虑如果遇到孤独者该怎么办\n
和/或一个单独的\r
,您的程序可以比现在更优雅地处理它。
关于C 程序 - printf 与其他 printf 字符重叠,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42496360/