#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
typedef int bool;
#define true 1
#define false 0
#define A 65
#define Z 90
#define a 97
#define z 122
#define NEWLINE 10
int main(int argc, char* argv[])
{
int noArgReverse();
int argReverse(int i, char* c[]);
if (argc == 1){
if (noArgReverse() == 0)
return 0;
else
return 1;
}
if (argc > 1){
if (argReverse(argc, argv) == 0)
return 0;
else
return 1;
}
else{
fprintf(stderr, "unknown error detected.\n");
return 1;
}
}
int noArgReverse()
{
char charInput[10000];
int pointerArray[5000];
int pointerCount = 0;
bool wordStart = false;
int indexer;
int lineLength;
int parser;
char currInput;
pointerArray[0] = 0; // first word would start at 0 be default
while (currInput != EOF){
lineLength = 0;
indexer = 0;
pointerCount = 0;
while ((currInput = getc(stdin)) != NEWLINE){
/*
* I am implementing a 10,000 char limit, as this seems an
* unreasonable length.
*/
if (lineLength == 9999){
fprintf(stderr, "Line length exceeded 10,000 chars. "
"This line and, if in the middle of a word,"
"will be split.\n");
break;
}
if (!wordStart){
if ((currInput >= A && currInput <= Z) || (currInput >= a && currInput <= z)){
wordStart = true;
}
}
while (wordStart){
charInput[lineLength++] = currInput;
currInput = getc(stdin);
//if the word has ended
if ((currInput < A || currInput > Z) && (currInput < a || currInput > z)){
wordStart = false;
charInput[lineLength++] = '\0';
if (pointerCount != 0){ // at least one word has been added
++indexer;
pointerArray[indexer] = pointerCount;
pointerCount = lineLength;
}
else //first word of the line to be added
pointerCount = lineLength;
}
}
}
while (indexer >= 0){
parser = pointerArray[indexer--];
while (charInput[parser] != '\0')
fprintf (stdout, "%c", charInput[parser++]);
fprintf (stdout, " ");
}
fprintf (stdout, "\r\n");
if (lineLength == 0){
currInput = EOF;
}
}
return 0;
}
int argReverse (int argc, char* argv[])
{
char charInput[10000];
int pointerArray[5000];
int pointerCount = 0;
bool wordStart = false;
int indexer;
int lineLength;
int parser;
char currInput;
FILE *currentFile;
while (argc > 0){
currentFile = fopen(argv[argc--], "r");
while ((currInput = getc(currentFile)) != EOF){
lineLength = 0;
indexer = 0;
pointerCount = 0;
while (currInput != NEWLINE){
/*
* I am implementing a 10,000 char limit, as this seems an
* unreasonable length for a single line.
*/
if (lineLength == 9999){
fprintf(stderr, "Line length exceeded 10,000 chars. "
"This line and, if in the middle of a word, the word, "
"will be split.\n");
break;
}
if (!wordStart){
if ((currInput >= A && currInput <= Z) || (currInput >= a && currInput <= z)){
wordStart = true;
}
}
while (wordStart){
charInput[lineLength++] = currInput;
currInput = getc(currentFile);
//if the word has ended
if ((currInput < A || currInput > Z) && (currInput < a || currInput > z)){
wordStart = false;
charInput[lineLength++] = '\0';
if (pointerCount != 0){ // at least one word has been added
++indexer;
pointerArray[indexer] = pointerCount;
pointerCount = lineLength;
}
else //first word of the line to be added
pointerCount = lineLength;
}
}
}
}
fclose(currentFile);
}
return 0;
}
因此,对于我的第一个函数,我遇到了一个错误,我在调试时似乎无法深入了解,或者更确切地说,我不确定如何解决。该函数应从 stdin
获取输入,并以相反的顺序打印单词(字符应保持顺序,因此“This is a sentence”应为“sentence a is This”)。很简单。然而,当我给出示例输入时,我得到的输出都是错误的。
输入:
This is sample
input for testing
输出:
testing for input sample is This
This
输入有一个回车,但输出的行与行之间多了一个回车,并且不分割行。
因此,它没有在应该打印换行符的时候打印换行符,而是在结束时再次打印第一个输入的单词。
我遇到的第二个问题是在第二组代码中,argReverse
函数。打开文件后,在本例中我使用 test.txt,这是一个简单的文本文件,包含几行短语和空行,第一次使用 getc
返回段错误。我读到这是权限或文件打开失败,但我不确定如何解决此问题。我试图首先打开最后一个文件,然后从那里开始工作,显然,这应该能够处理多个文件,但我什至无法打开一个。我不确定该怎么做才能解决这个问题。我试过将 getc
移到 while
循环之外,同样的问题。我猜我在打开文件时做错了什么,但我不知道它是什么。
最佳答案
风格说明:
bool
类型,和 true
和 false
在 <stdbool.h>
中定义.
使用像 'A' 'Z' 'a' 'z' '\n'
这样的字符常量而不是硬编码数字,和/或使用字符分类功能,如 isalpha
来自 <ctype.h>
.
“反向”函数在结束时只返回 0,因此返回任何东西都没有意义。它们应该被声明为返回 void
.如果他们确实返回了一些有用的东西,我会从 main
返回那个值(消除 if
语句)。例如,
if ( argc == 1 )
return noArgReverse();
将大型数组放在堆栈上通常不是一个好主意。 (大是主观的,但我根据经验使用 2K 字节。)对于不可重入函数,您可以将数组声明为 static
。让他们离开堆栈。对于可重入函数,您可以 malloc
数组,和 free
他们在最后。
设计注意事项:
fgets
函数将读取一行并将其放入缓冲区。无需一次读取一个字符。
处理命令行参数时,规范循环是
int main( int argc, char *argv[] )
{
for ( int i = 1; i < argc; i++ )
printf( "argv[%d] is \"%s\"\n", i, argv[i] );
}
您的段错误的原因是您正在使用 argv[argc]
,C 规范保证为 NULL
.所以你正在传递 NULL
至 fopen
.此外,您应该始终检查 fopen
的返回值。 ,因为 fopen
将返回 NULL
如果无法打开文件。
到目前为止,代码中最大的设计问题是重复。你有两个几乎相同的功能,这对调试和维护来说是一场噩梦,因为每次更改都需要进行两次,并测试两次。解决方案是定义一个 reverse
将文件指针作为输入的函数。 main
函数应该负责打开/关闭文件,或者可以传递 stdin
当没有任何参数时。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXL 10000
#define MAXW 5000
void reverse( FILE *fp );
int main( int argc, char *argv[] )
{
if ( argc < 2 )
{
reverse( stdin );
return 0;
}
FILE *fp;
for ( int i = 1; i < argc; i++ )
{
printf( "----- %s -----\n", argv[i] );
if ( (fp = fopen( argv[i], "r" )) == NULL )
{
printf( "***Error: unable to open file\n" );
}
else
{
reverse( fp );
fclose( fp );
}
}
return 0;
}
void reverse( FILE *fp )
{
static char line[MAXL]; // buffer for the input line
static char *word[MAXW]; // array of pointers to the words on the line
while ( fgets( line, MAXL, fp ) != NULL )
{
int i = -1;
int count = 0; // count of words on the line
for (;;)
{
// skip any non-alpha characters
for ( i++; line[i]; i++ )
if ( isalpha( line[i] ) )
break;
// check if we've reached the end of the line
if ( !line[i] )
break;
// add the pointer to the word list
word[count++] = &line[i];
// scan till we reach the end of the word
for ( i++; line[i]; i++ )
if ( !isalpha( line[i] ) )
break;
// check if we've reached the end of the line
if ( !line[i] )
break;
// terminate the word
line[i] = '\0';
}
// output the words in reverse order
for ( i = count - 1; i >= 0; i-- )
printf( "%s ", word[i] );
printf( "\n" );
}
}
关于c - Stdin with getc 产生额外的输出,并打开文件导致 C 中的段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29908211/