I have been learning from the C Programming Language book (K&R) and was writing one of the exercises that removes trailing blanks from an input. I understand that a segmentation fault is at some level a problem having to do with accessing memory that is not accessible, but I have read through this code several times and can't find the error. I would like it very much if someone could help find this error and tell me how to discover errors like this in the future.
#include <stdio.h>
#define MAXLINE 1000
#define CHAR 0 /*character definition*/
#define TRAIL 1 /*determines whether program is in a trailing blank*/
int getinput(char input[], int max);
int trailrem(char input[], char copyto[]);
int len;
int main() {
char line[MAXLINE]; /*current line*/
char newline[MAXLINE];
int i, c, newreturn; /*integer counter, character holder, current line length, and trailrem return value*/
int len;
while((len = getinput(line, MAXLINE)) > 0) {
newreturn = trailrem(line, newline);
for(i = 0; i <= newreturn; ++i)
printf("\n%c\n", newline[i]);
}
}
int getinput(char input[],int max) {
int i, c, line;
for(i = 0; (c = getchar()) != EOF && c != '\n' && c < (max-1); ++i)
input[i] = c;
if(c == '\n') {
input[i] = c;
++i;
}
input[i] = '\0';
return i;
}
int trailrem(char input[], char copy[]) {
int i, j, minusin, state, r;
for(i = len; input[i] != EOF && i >= 0; --i) {
if(input[i] =='\n')
state = TRAIL;
else if((input[i] == ' ' && state == TRAIL) ||( input[i] == '\t' && state == TRAIL))
++minusin;
else if(state == TRAIL && (input[i] != ' ' || input[i] != '\t'))
state = CHAR;
for(j = (r = len-minusin); state == CHAR; --j){
copy[j-2] = input[i];
}
}
copy[r] = '\0';
copy[r-1] = '\n';
return r;
}
So many problems in your code. But the main problem is, you have a global len
int len;
And a local len in the main function.
You are initializing len in main function like this:
while((len = getinput(line, MAXLINE)) > 0)
So the local len is updated. But the global len is still 0.
You are expecting that, you will get the updated value of len in trailrem method but you don't. In trailrem() you will get len equal to 0!
for(i = len; input[i] != EOF && i >= 0; --i)
So i is 0 too. And hence, copy[r-1] = '\n'; will crash, because r-1 can be negative.
Other problems: (BLUEPIXY and WhozCraig mentioned in the comment).
for(i = 0; (c = getchar()) != EOF && c != '\n' && c < (max-1); ++i)
here, c < (max-1) should be i < (max-1).
++minusin; in trailrem function where minusin is uninitialized.
Related
I am currently learning C and working on a problem that breaks input lines into lengths of n. Below is my current code where n is set to 30. When it reaches the n-th index it replaces that index with ' ' and then line breaks, but it will only do it for the first n characters and I'm unsure what isn't getting rest in order to it to continue making a new line at the nth index.
int getline2(void);
int c, len, cut, counter;
char line[MAXLINE];
main() {
while ((len = getline2()) > 0) {
if (len > BREAK) {
c = 0;
counter = 0;
while (c < len) {
if (line[c] == ' ') {
counter = c;
}
if (counter == BREAK) {
line[counter] = '\n';
counter = 0;
}
counter++;
c++;
}
}
printf("%s", line);
}
return 0;
}
int getline2(void) {
int c, i;
extern char line[];
for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
line[i] = c; //i gets incremented at the end of the loop
if (c == '\n') {
line[i] = c;
++i;
}
line[i] = '\0';
return i;
}
Your code is a little too complicated:
you do not need to store the bytes read from the file into an array, just output them one at a time, keeping track of the line length
when the line would become too long, output a newline and reset the count before you output the byte.
also not that none of these global variables deserves to be global.
and the prototype for main should be either int main(), int main(void) or int main(int argc, char *argv[]) or equivalent. main()` is an obsolete syntax that should be avoided.
Here is a modified version:
#include <stdio.h>
#define BREAK 30
int main() {
int c;
int len = 0;
while ((c = getchar()) != EOF) {
if (c == '\n') {
putchar(c);
len = 0;
} else {
if (len >= BREAK) {
putchar('\n');
len = 0;
}
putchar(c);
len++;
}
}
return 0;
}
I have experienced some problem with segmentation when I'm learning through C, my aim is to swap the tabs in the program with spaces:
I have used the get_line template and modified the code to suit the situation. Here is the whole coded solution:
#include <stdio.h>
#define MAXLINE 1000
char line[MAXLINE];
char detabline[MAXLINE];
int get_line(void);
int main(void){
int len;
int i;
int nt = 0;
extern char detabline[];
extern char line[];
while ((len = get_line()) > 0){
for (i = 0; i < len; ++i){
if (line[i] == '\t'){
printf("%s", " ");
}
else{
printf("%s", line[i]);
}
}
}
return 0;
}
int get_line(void){
int c, i, nt;
nt = 0;
extern char line[];
for (i = 0; i < (MAXLINE - 1) && (c = getchar()) != EOF && ((c != '\t') || (c != '\n')); ++i){
line[i] = c;
}
if (c == '\n'){
line[i] = c;
++i;
}
else if (c == '\t'){
++nt;
}
line[i] = '\0';
return i;
}
The problem is to locate which memory isn't allocated correctly. I may have some redundant code in the solution by the way.
regarding:
for (i = 0; i < (MAXLINE - 1) && (c = getchar()) != EOF && ((c != '\t') || (c != '\n')); ++i){
this expression:
(c != '\t')
will result in no tab character ever being in the line[] array.
this expression:
(c != '\n')
will result in no newline character sequence ever being in the line[] array.
then, due those expressions, the line[] array will not be updated (ever again) when a tab or a newline is encountered due to those expressions causing an early exit from the for() loop
The following proposed code:
cleanly compiles
performs the desired functionality
and now, the proposed code:
#include <stdio.h>
#define MAXLINE 1000
char line[MAXLINE];
int main(void)
{
int i = 0;
int ch;
while ( i< MAXLINE-1 && (ch = getchar()) != EOF && ch != '\n' )
{
if ( ch == '\t')
{
line[i] = ' ';
}
else
{
line[i] = (char)ch;
}
i++;
}
printf( "%s\n", line );
}
Post a comment if you want further details about the proposed code.
#include <stdio.h>
#define MAXLENGTH 1000
void get_input(char ch[], int maxline);
void replace_blanks(char ch[], int z);
int main(void)
{
char ch[MAXLENGTH];
int z;
z = 0;
printf("Please enter some input. ");
get_input(ch,MAXLENGTH);
replace_blanks(ch,MAXLENGTH);
while(putchar(ch[z]) != EOF)
{
++z;
}
return 0;
}
void get_input(char ch[], int lim)
{
int z, i;
z = 0;
while((i = getchar()) != EOF && z < lim-1 && i != '\n')
{
ch[z] = i;
z++;
}
if(i == '\n')
{
ch[z] = i;
z++;
}
ch[z] = '\0';
}
void replace_blanks(char ch[], int lim)
{
char ch_copy[lim];
int i, arrayindex, copy_counter;
copy_counter = 0;
arrayindex = 0;
i = arrayindex;
while(ch[arrayindex] != EOF && arrayindex < lim-1){
if(ch[arrayindex] != ' '){
ch_copy[copy_counter] = ch[arrayindex];
i = arrayindex;
arrayindex++;
copy_counter++;
}
else if(ch[arrayindex] == ' ' && ch[i] != ' '){
ch_copy[copy_counter] = ch[arrayindex];
i = arrayindex;
arrayindex++;
copy_counter++;
} else {
i = arrayindex;
arrayindex++;
}
}
ch_copy[arrayindex] = '\0';
for(arrayindex = 0; ch_copy[arrayindex] != EOF && arrayindex < lim-1; arrayindex++){
printf("%d", arrayindex);
ch[arrayindex] = ch_copy[arrayindex];
}
ch[arrayindex] = '\0';
}
Output from this functon from the printf() is
Please enter some input. hello hi
0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998hello hi
Segmentation fault: 11
No no, declare ch as char[]:
char ch[MAXLENGTH];
While possible, it's definitely possible to store strings in an int array,
you shouldn't do that at all.
I also would write your get_input like this:
int get_input(char *ch, size_t lim)
{
if(!fgets(ch, lim, stdin))
return 0;
// removing possible \n
ch[strcspn(ch, "\n")] = 0;
return 1;
}
The logic behind the algorithm seems to be OK, however your while and for
conditions are a little bit strange and it caused problems when stepping through
the debugger.
while(ch[arrayindex] != EOF && arrayindex < lim-1)
Don't check for EOF, you shouldn't write an EOF in
a string, because EOF is an signed int and doesn't fit in a char. If you
read a line with fgets, there is never going to be a EOF in the string. Also
because you did while((i = getchar()) != EOF && z < lim-1 && i != '\n') in
the read function, you are not going to have an EOF in the string. You should
check for '\0' which is the only correct way to terminate a string.
The correct condition would be:
while(ch[arrayindex] != '\0' && arrayindex < lim-1)
If you are checking for the bounds, then it would be better to have that first
and then check for the end of string.
while(arrayindex < lim-1 && ch[arrayindex])
Here I omitted the != '\0' because '\0' == 0.
After the loop you should do
ch_copy[copy_counter] = '\0';
Use copy_counter, not arrayindex, because arrayindex is the index for the
original, where multiple blanks are stored. copy_counter is the index for the
copy.
But if prior of the while loop you did:
memset(ch_copy, 0, lim);
then the whole buffer would be initalized with 0 and you won't need to worry
about the 0 terminating because the copy will never be larger than the original.
Lastly, the condition in the for loop should be corrected with the same logic
as for the while loop. Here you don't need to check for the boundaries,
because you know that the copy is never be larger than the original:
for(arrayindex = 0; ch_copy[arrayindex]; arrayindex++){
ch[arrayindex] = ch_copy[arrayindex];
}
ch[arrayindex] = 0;
Or you could just use strcpy:
strcpy(ch, ch_copy);
So the function should look like this:
void replace_blanks(char ch[], int lim)
{
char ch_copy[lim];
int i, arrayindex, copy_counter;
memset(ch_copy, 0, lim);
copy_counter = 0;
arrayindex = 0;
i = arrayindex;
while(arrayindex < lim -1 && ch[arrayindex]){
if(ch[arrayindex] != ' '){
ch_copy[copy_counter] = ch[arrayindex];
i = arrayindex;
arrayindex++;
copy_counter++;
}
else if(ch[arrayindex] == ' ' && ch[i] != ' '){
ch_copy[copy_counter] = ch[arrayindex];
i = arrayindex;
arrayindex++;
copy_counter++;
} else {
i = arrayindex;
arrayindex++;
}
}
strcpy(ch, ch_copy);
}
I'm pretty novice. I've been working through the K&R C programming book, and one of the exercises was to write a program that prints any input lines that are longer than 80. Here's my code:
include <stdio.h>
#define MAXLINE 1000
int getaline(char line[], int maxline);
int main()
{
int i, c;
char line[MAXLINE];
if ((c = getaline(line, MAXLINE)) > 80){
for (i =0; i < MAXLINE; ++i)
if (c != '\0')
printf("%c", line[i]);
printf("\n");
}
}
/* reads a line into S, returns the length of that line */
int getaline(char s[], int lim)
{
int c, i;
for (i = 0; i < lim-1 && (c = getchar()) != EOF && c != '\n'; ++i)
s[i] = c;
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
So I'll pipe a line longer than 80 chars to the compiled program. Here's the output:
cat input.txt | ./a.out
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
??i?F???4?w??>&Y?_???xf?7U?h#?
??i??7U?v??i??7U??7U?(?7U?#?
H??i?8?7U??7U?
It prints the line, but it gives all this anomalous output. I tried to figure out why, but I just can't seem to find why.
However, I am pretty sure that the problem lies in the getaline function.
Any help would be greatly appreciated! :)
You are confusing c with the current character.
Try this, change this
for (i =0; i < MAXLINE; ++i)
with
for (i = 0 ; i < c ; ++i)
and the check should be
if (line[i] != '\0')
instead of
if (c != '\0')
your getaline() function returns, i the position of the last character read, you are comparing it with the null termination byte.
Also if you are null terminating the string, why don't you just
printf("%s\n", line);
this code should work
#include <stdio.h>
#define MAXLINE 1000
int getaline(char line[], int maxline);
int main()
{
int i, c;
char line[MAXLINE];
if ((c = getaline(line, MAXLINE)) > 80)
printf("%s\n", line);
return 0; // you must return from main()
}
/* reads a line into S, returns the length of that line */
int getaline(char s[], int lim)
{
int c, i;
for (i = 0 ; (i < lim - 1) && ((c = getchar()) != EOF) && (c != '\n') ; ++i)
s[i] = c;
if (c == '\n')
{
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
You're checking c as though it were a character, when it's the line-length count; it will never be equal to \0, so you keep pringting.
if ((c = getaline(line, MAXLINE)) > 80){
// c now equals, let's say, 81
for (i =0; i < MAXLINE; ++i)
// c is still 81, we never fail
if (c != '\0')
printf("%c", line[i]);
printf("\n");
}
Consider this instead:
if (getaline(line, MAXLINE) > 80) {
for (i =0; i < MAXLINE; ++i)
{
c = line[i];
if (c != '\0')
printf("%c", c;
else
break;
}
printf("\n");
}
Replace
for (i =0; i < MAXLINE; ++i)
if (c != '\0')
printf("%c", line[i]);
by
for (i =0; i < c; ++i)
printf("%c", line[i]);
I'm on to K&R's Exercise 1-18
Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.
This is what I've came up with so far
#include <stdio.h>
#define MAXLINE 1000
int getline(char line[], int maxline);
void copy(char to[], char from[]);
int main () {
int len;
char line[MAXLINE];
while (getline(line, MAXLINE) > 0) {
printf("%s", line);
}
return 0;
}
int getline(char s[], int lim) {
int c, i, lastNonBlankIndex;
lastNonBlankIndex = 0;
for (i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
if (c != ' ' && c != '\t') {
lastNonBlankIndex = i + 1;
}
s[i] = c;
}
if (i != lastNonBlankIndex) {
i = lastNonBlankIndex;
c = '\n';
}
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
The second part sounded hard, as I wasn't sure what I should return if the line only has blanks or tabs. After all, if I return 0, it will halt the getline() calling. Would this be where I should set up a #define, such as ALL_BLANKS.
Anyway, to actual main question, is this a correct way to remove trailing blanks and tabs from lines? I ran a few inputs through, and it seemed to work. However, if I copy and paste text with newlines into the CL, it appears all strung together. And when I type a line into the CL and push enter, it automatically prints it. Should I be building an array of lines, and then looping through and printing them when done ?
Your code looks correct, but I think it would be better if you separate the operations of reading a line from stdin and stripping the line of trailing whitespace (decoupling). Then you can use the unmodified getline from the book (code reuse) and won't have the problem of halting on returning 0.
And if you are interested in other solutions, the CLC-wiki has an almost complete list of K&R2 solutions.
#include <stdio.h>
#define MAXLINE 1024
int getline(char s[], int lim);
main()
{
int i, len;
char line[MAXLINE];
while ((len = getline(line, MAXLINE)) > 0) {
i = len - 2;
while (i >= 0 && (line[i] == ' ' || line[i] == '\t'))
--i;
if (i >= 0) {
line[i+1] = '\n';
line[i+2] = '\0';
printf("%s", line);
}
}
return 0;
}
This is the category 1 solution I wrote some time ago. getline is as on page 28 of the book. It might be nicer to put the removal of whitespace in a separate function rstrip, but I leave this as an exercise for the reader.
Your basic design is sound. It is better, as you did, to print a stripped line as soon as you've built it, so that your program only needs to keep one line at a time in memory and not the whole file.
There is a small problem with your code: it doesn't implement the second part of the question (“delete entirely blank line”). That's because you always tack a '\n' at the end of the string. This is easy to fix, but remember that you must return a nonzero value to your caller since a blank line doesn't indicate the end of the file.
getline should return -1 (a negative value in general) if there is an error or if EOF is reached. Then your loop conditional can check that it returns something >= 0 and still allow for 0 length lines.
for (i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
I almost never include an assignment within a loop conditional. I would rather add 10 lines of code to get around doing that because it's difficult to read. I would especially refrain from using them with complicated conditionals.
int i = 0;
while (i < lim) {
c = getchar();
if (c == EOF || c == '\n') {
break;
}
line[i] = (char)c;
i++;
}
line[i] = '\0'; // Null terminate the string
This code should read in a line for you. I would separate the reading in of the line from the removal of the trailing white space. You could very easily work backwards from the end of the string to remove white spaces at the location where I null terminated the line, since after having read in the line you now know its length. Essentially you grow the string and then you prune it back down after it has finished growing.
This is how i did it.
#include <stdio.h>
#define MAXLINE 1000
#define IN 1
#define OUT 0
int state = OUT;
int getline(char s[], int lim);
void copy(char to[], char from[]);
int main(void)
{
int lenght;
int max = 0;
char line[MAXLINE];
char longest[MAXLINE];
while ((lenght = getline(line, MAXLINE)) > 0)
if (lenght > max)
{
max = lenght;
copy(longest, line);
}
if (max > 0)
printf("\n%s\n", longest);
return 0;
}
int getline(char s[], int lim)
{
int i, c;
for (i = 0; i < lim - 1 && ((c = getchar()) != EOF) && (c != '\n'); i++)
{
if (state == IN && c != ' ' && c != '\t')
{
s[i] = ' ';
i++;
state = OUT;
}
if (s[0] == ' ')
{
s[0] = '\b';
}
s[i] = c;
if (c == ' ' || c == '\t')
{
i--;
state = IN;
}
}
if (c == '\n')
{
s[i] = c;
i++;
}
s[i] = '\0';
return i;
}
void copy(char to[], char from[])
{
int i = 0;
while ((to[i] = from[i]) != '\0')
i++;
}
#include <stdio.h>
#define MAXLINE 1000
size_t getline(char *s,size_t lim)
{
if( fgets(s,lim,stdin) )
{
while( *s && strchr(" \t\n",s[strlen(s)-1]) )
s[strlen(s)-1]=0;
return strlen(s);
}
return 0;
}
main()
{
int len;
char line[MAXLINE];
while (getline(line,sizeof line)) {
printf("%s", line);
}
return 0;
}