I'm trying to read last n lines from file and then print them. To read the lines I'm using fgets() and it seems to work fine. However, when I try to print the last n lines that I have stored in the array, it only prints the last line n times. It seems like there is something wrong with the way I store strings in an array. Here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int agrc, char** agrv) {
FILE* input;
input = fopen(agrv[1], "r");
int n = *agrv[2]-'0';
int line = 0;
char text[11];
char** tab = malloc(1000000*sizeof(text));
while(fgets(text, sizeof(text), input) != 0) {
tab[line] = text;
line++;
}
fclose(input);
int jump = line-n;
for(int i=jump; i<line; i++) {
printf("%s\n", tab[i]);
}
}
Any help would be appreciated. Thanks in advance!
EDIT:
I've changed my while loop to this. However, it still doesn't work.
while(fgets(text, sizeof(text), input) != 0) {
char text2[11];
strcpy(text2, text);
tab[line] = text2;
line++;
}
tab[line] = text; sets tab[line] to point to the start of text. So you end up with all the tab[i] pointing to the same place, the start of text.
You need to copy each line read from the file to a different place in memory.
It may increase your understanding if you see working code that does what you hope to achieve. Because your OP could only show 1-9 "last lines", this code doesn't try to go beyond that "meager" amount. Further, this code is meant for files whose lines "are of moderate length". It should be clear where changes can be enacted.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h> // for 'isdigit()'
// Factor processing into functions for clarity
void tail( FILE *ifp, size_t n ) {
char text[ 9 ][ 126 + 1 + 1 ]; // 9 'rows' up to 128 bytes each
size_t lnCnt = 0;
while( fgets( text[ lnCnt % 9 ], sizeof text[0], ifp ) != NULL )
lnCnt++;
// Do the math to workout what is wanted and what's available
if( lnCnt < n )
n = lnCnt, lnCnt = 0;
else
lnCnt += 9 - n;
// output
while( n-- )
printf( "%s", text[ lnCnt++ % 9 ] );
}
int main( int argc, char *argv[] ) {
// Check parameters meet requirements
if( argc != 3 || !isdigit( argv[ 2 ][ 0 ] ) ) {
fprintf( stderr, "Usage: %s filename #lines (1-9)\n", argv[ 0 ] );
exit( EXIT_FAILURE );
}
// Check functions didn't fail
FILE *ifp = fopen( argv[ 1 ], "r" );
if( ifp == NULL ) {
fprintf( stderr, "Cannot open '%s'\n", argv[ 1 ] );
exit( EXIT_FAILURE );
}
// do processing
tail( ifp, argv[ 2 ][ 0 ] - '0' );
// clean up
fclose( ifp );
return 0;
}
Related
I need to create a c program to find 'a' character in a sentence from another file. I have done the coding but still, do not get the correct result. What is wrong with my coding? the sentence in the okeya.txt is kazega hukeba okeyaga moukaru, but when I compile the program, the result is "found at 85"
#include <stdio.h>
#include <stdlib.h>
#define x_size 80
int main( void ){
char filename[] = "okeya.txt"; FILE *fp;
char input[ x_size ];
char find = 'a';
char *poin;
poin=filename;
char *p = input;
if( (fp = fopen( filename,"r" ) ) == NULL ){ printf( "?????????????" );
exit( -1 );
}
while( fgets( input , x_size , fp ) != NULL );
fclose(fp);
while(*poin!= '\0'){
if(*poin == find){
printf("\n found at %d .\n", poin-input + 1);
}
poin ++;
}
return 0;
}
I have a file with 3 lines in it, I'm tring to read this file and save each line as a separate string.
here is what I tried to do, it does save the first line but it overrides it by saving the first line and the second line' and I can't get my head around on how to do save each line as an individual string , and also I'm getting an error->
* stack smashing detected *: /home/ubuntu/workspace/ex12.c.o terminated
Aborted
#include <stdio.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include<stdio.h>
#include<fcntl.h>
#include<errno.h>
#include <unistd.h>
extern int errno;
int main( int argc, char *argv[] ) {
char *path1;
char firstline[80];
char secondline[80];
char thirdline[80];
printf("Program name %s\n", argv[0]);
if( argc == 2 ) {
printf("The path of the config file that you supplied is %s\n", argv[1]);
}
else if( argc > 2 ) {
printf("Too many arguments supplied.\n");
}
else {
printf("One argument expected.\n");
}
int fd1 = open(argv[1], O_RDONLY | O_CREAT);
if (fd1 ==-1)
{
// print which type of error have in a code
printf("Error Number % d\n", errno);
// print program detail "Success or failure"
perror("Program");
exit(EXIT_FAILURE);
}
else {
char c;
int i=0;
while ((read(fd1, &c, 1) == 1) )
{
firstline[i++]=c;
if(c=='\n')
{
//printf("end of line");
printf("%s",firstline);
}
}
}
int close(int fd1);
return 0;
}
NOTE: I DO NOT WANT to use fopen,fgets,sscanf or getline.
Any help would be appreciated
Here is an example to demonstrate the brake from first loop from your example:
#define MAXLENGTH 80
...
char firstline[MAXLENGTH + 1] = {0};
char secondline[MAXLENGTH + 1] = {0};
char thirdline[MAXLENGTH + 1] = {0};
....
else {
char c;
int i=0;
while ((read(fd1, &c, 1) == 1 && i < MAXLENGTH)
{
firstline[i++]=c;
if(c=='\n')
{
break; /* break from first loop */
}
}
/* add a '\0' to the end of the string! */
firstline[i] = '\0';
//printf("end of line");
printf("%s",firstline);
i=0;
while ((read(fd1, &c, 1) == 1 && i < MAXLENGTH)
{
secondline[i++]=c;
if(c=='\n')
{
break; /* break from second loop */
}
}
/* add a '\0' to the end of the string! */
secondline[i] = '\0';
printf("%s",secondline);
i = 0;
int i=0;
while ((read(fd1, &c, 1) == 1 && i < MAXLENGTH)
{
thirdline[i++]=c;
if(c=='\n')
{
break; /* break from third loop */
}
}
/* add a '\0' to the end of the string! */
thirdline[i] = '\0';
//printf("end of line");
printf("%s",thirdline);
}
...
printf prints until a NULL terminating character is seen. Your printf goes over unallocated memory areas because you are not inserting a NULL(value 0) at the end of the string.
Also, you forgot to reinitialize i to 0.
while ((read(fd1, &c, 1) == 1) )
{
firstline[i++]=c;
if(c=='\n')
{
firstline[i] = 0;
i = 0;
//printf("end of line");
printf("%s",firstline);
}
}
The reason you are getting the error is most likely because you never reset i back to 0 so you keep reading more than 80 characters in to firstline
As to saving each line in to its own string, your just need to use you other variables, instead of using firstline all the time.
There a couple of ways to do that:
When you detect end of line exit the loop (with break), and start another loop where you will put the characters in secondline. Do the same for third.
If you already learned a bit about pointers, you can keep one loop, but use an extra variable, like char *currentLine that will hold the address of the array you want to read in to. Change the address every time you detect end of line like this: currentLine = secondline.
Also remember to put '\0' at the end of every line you read, or your program may print garbage when you try to print what you read to the screen.
the following proposed code:
cleanly compiles
performs the desired functionality
proper checks for and handles errors
gives 'magic' numbers (3, 80) meaningful names
and now, the proposed code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#define MAX_LINES 3
#define MAX_LINE_LEN 80
int main( int argc, char *argv[] )
{
char Lines[ MAX_LINES ][ MAX_LINE_LEN ] = { '\0' };
if( argc != 2 )
{
fprintf( stderr, "USAGE: %s <configFileName>\n", argv[0] );
exit( EXIT_FAILURE );
}
// implied else, correct number of command line arguments
int fd1 = open(argv[1], O_RDONLY );
if (fd1 ==-1)
{
perror( "open failed" );
exit(EXIT_FAILURE);
}
// implied else, open successful
char c;
for( int i = 0; i < MAX_LINES; i++ )
{
for( int j=0; j< MAX_LINE_LEN; j++ )
{
ssize_t bytecount = read(fd1, &c, 1 );
if( bytecount < 0 )
{
perror( "read failed" );
close( fd1 );
exit( EXIT_FAILURE );
}
// implied else, read successful
Lines[ i ][ j ] = c;
if( c == '\n' )
{
break;
}
}
}
for( int i = 0; i< MAX_LINES; i++ )
{
printf( "%s\n", Lines[i] );
}
close( fd1 );
return 0;
}
Note: this code assumes that each line is less than 80 characters
running the proposed code against its' own source file results in:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
Understanding handling direct pointers in C
Here is a code that works for an array of strings for fixed number of items and fixed line length :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXNAMELEN 100
#define MAXLINELEN 100
#define MAXITEMS 1000
int main(int argc, char ** argv) {
FILE * infile, * outfile;
char name[MAXNAMELEN];
char line[MAXLINELEN];
char lines[MAXITEMS][MAXLINELEN];
int i, items = 0;
printf("Enter a source filename: ");
fgets(name, sizeof(name), stdin);
name[strlen(name)-1] = '\0'; // strip newline
infile = fopen(name, "r");
while (fgets(line, sizeof(line), infile)) {
strcpy(lines[items], line);
items++;
}
qsort(lines, items, MAXLINELEN, strcmp);
printf("Enter a destination filename: ");
fgets(name, sizeof(name), stdin);
name[strlen(name)-1] = '\0'; // strip newline
outfile = fopen(name, "w");
for (i=0; i<items; i++) {
fputs(lines[i], outfile);
}
fclose(infile);
fclose(outfile);
}
Problem description and code
If I try to read an input.txt file that is within MAXLINELEN and MAXITEMS, the program works fine. Now imagine I am reading from a much larger "inputfile" line by line where maximum line length could be anything, then I would have to use a character pointer (char*) to read the input. char* linesptr[MAXITEMS];
Here is my code where I am trying to accomplish reading from an input file line by line delimited by a newline character.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#define MAXNAMELEN 1000
#define MAXLINELEN 1000
#define MAXITEMS 100000
char* linesptr[MAXITEMS];
int
main(int argc, char ** argv) {
FILE * infile, * outfile;
char name[MAXNAMELEN];
char line[MAXLINELEN];
int i, items = 0;
printf("Enter a source filename: ");
fgets(name, MAXNAMELEN, stdin);
name[strlen(name)-1] = '\0'; // strip newline
printf("%s infile \n",name);
infile = fopen(name, "r");
while (fgets(line, MAXLINELEN, infile)) {
int length = strlen(line);
line[length-1] = '\0';
linesptr[items] = line; *<- I am writing to the same mem location*
printf("the input string %d is : %s \n",items, linesptr[items]);
items++;
}
qsort(linesptr, items, MAXLINELEN, strcmp);
printf("Enter a destination filename: ");
fgets(name, sizeof(name), stdin);
name[strlen(name)-1] = '\0'; // strip newline
outfile = fopen(name, "w");
for (i=0; i<items; i++) {
fputs(linesptr[i], outfile);
}
fclose(infile);
fclose(outfile);
}
PROBLEM
I am copying the pointer address into the nth cell of the array linesptr where nth is the value=items (Here is the reference line from the code: linesptr[items] = line;). so when you print the final answer, I am referencing the same memory address to the buffer named line, the memory location at line will always point to the most recent fgets(). I understand the error but I do not know how to fix the issue. I would appreciate any help to fix the bug in the code.
Copy the line to a dynamically-allocated string.
while (fgets(line, MAXLINELEN, infile)) {
int length = strlen(line);
if (length > 0 && line[length-1] == '\n') {
line[length-1] = '\0';
length--;
}
char *linecopy = malloc(length+1);
strcpy(linecpy, line);
linesptr[items] = linecpy;
printf("the input string %d is : %s \n",items, linesptr[items]);
items++;
}
And if you want to handle more than MAXITEMS lines, you should allocate linesptr using malloc() as well. When you get to the current size of linesptr you can use realloc() to make it longer. See Read unknown number of lines from stdin, C for detailed code.
See How to qsort an array of pointers to char in C? for the proper way to sort an array of pointers to strings.
You ask for a example, so here it is:
the following proposed code:
is written for readability
checks for and handles error conditions
makes use of getline() and realloc()
is not as efficient as it could be because it calls realloc() for every line in the source file.
properly/safely uses strcspn() for removing any (possible) trailing newline characters
could have simplified the code by extracting the 'cleanup' to a sub function rather than repeating the same 'cleanup' code when ever an error was encountered.
used size_t rather than int for indexes into arrays to avoid implicit conversions
minimized the scope of variables where possible
passes proper third parameter to qsort()
properly implements the compare() helper function for qsort()
and now, the proposed code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXNAMELEN 1024
// prototypes
int compare(const void *, const void *);
int main( void )
{
printf("Enter a source filename: ");
char name[ MAXNAMELEN ];
if( !fgets(name, sizeof( name ), stdin) )
{
perror( "fgets for input file name failed" );
exit( EXIT_FAILURE );
}
// implied else, fgets for input file name successful
name[strcspn( name, "\n" ) ] = '\0'; // strip newline
printf("%s infile \n",name);
FILE *fp_in = fopen(name, "r");
if( !fp_in )
{
perror( "fopen for input file failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen for input file successful
char **linesarray = NULL;
size_t numLines = 0;
char *line = NULL;
size_t lineLen = 0;
while( getline( &line, &lineLen, fp_in ) != -1 )
{
char ** temp = realloc( linesarray, (numLines+1) * sizeof( char* ) );
if( !temp )
{
perror( "realloc failed" );
fclose( fp_in );
for( size_t i = 0; i< numLines; i++ )
{
free( linesarray[i]);
}
free( linesarray );
exit( EXIT_FAILURE );
}
// implied else, realloc successful
linesarray = temp;
linesarray[ numLines ] = line;
numLines++;
// prep for next iteration
line = NULL;
lineLen = 0;
}
free( line );
fclose( fp_in );
//puts( "all file read in" );
qsort( linesarray, numLines, sizeof( char * ), compare );
//puts( "file sorted" );
printf("Enter a destination filename: ");
if( !fgets(name, sizeof(name), stdin) )
{
perror( "fgets for output file name failed" );
for( size_t i = 0; i< numLines; i++ )
{
free( linesarray[i]);
}
free( linesarray );
exit( EXIT_FAILURE );
}
// implied else, fgets() for output file name successful
name[strcspn( name, "\n" ) ] = '\0'; // strip newline
FILE *fp_out = fopen(name, "w");
if( !fp_out )
{
perror( "fopen for output file failed" );
for( size_t i = 0; i< numLines; i++ )
{
free( linesarray[i]);
}
free( linesarray );
exit( EXIT_FAILURE );
}
// implied else, fopen for output file successful
for (size_t i=0; i<numLines; i++)
{
if( fputs(linesarray[i], fp_out ) == EOF )
{
perror( "fputs failed" );
fclose( fp_out );
for( size_t i = 0; i< numLines; i++ )
{
free( linesarray[i]);
}
free( linesarray );
exit( EXIT_FAILURE );
}
}
fclose( fp_out );
for( size_t i = 0; i< numLines; i++ )
{
free( linesarray[i]);
}
free( linesarray );
}
int compare(const void *ls, const void *rs )
{
char *leftSide = *(char**)ls;
char *rightSide = *(char**)rs;
return strcmp( leftSide, rightSide );
}
Here is the complete working solution to read in a file (big data), sort it and write it to a file:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#define MAXNAMELEN 1000
#define MAXLINELEN 5000
#define MAXITEMS 100000
char* linesptr[MAXITEMS];
int compare_function(const void *name1, const void *name2)
{
const char *name1_ = *(const char **)name1;
const char *name2_ = *(const char **)name2;
return strcmp(name1_, name2_);
}
int
main(int argc, char ** argv)
{
FILE * infile, * outfile;
char name[MAXNAMELEN];
char line[MAXLINELEN];
int i, items = 0;
printf("Enter a source filename: ");
fgets(name, MAXNAMELEN, stdin);
name[strlen(name)-1] = '\0'; // strip newline
infile = fopen(name, "r");
while (fgets(line, MAXLINELEN, infile)) {
int length = strlen(line);
line[length-1] = '\0';
char *linecopy = malloc(length);
strcpy(linecopy, line);
linesptr[items] = linecopy;
items++;
}
qsort(linesptr, items, sizeof(char *), compare_function);
printf("Enter a destination filename: ");
fgets(name, sizeof(name), stdin);
name[strlen(name)-1] = '\0'; // strip newline
outfile = fopen(name, "w");
for (i=0; i<items; i++) {
fprintf(outfile, "%s\n", linesptr[i]);
}
fclose(infile);
fclose(outfile);
}
I have a homework and i can't really find where the problem with the code is. The main problem is to read 3 lines from a text file and use them to build a binary tree. The text file has these lines:
7
2 4 0 0 7 0 0
3 5 6 0 0 0 0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
const char* p;
const char* v1;
const char* v2;
char buf[100];
FILE *fptr = fopen("sd.in", "r");
if (fptr == NULL) {
printf("Failed to open file\n");
return -1;
}
if(fgets(buf,100,fptr)!=NULL)
p=strtok(buf,"\n");
printf("%s\n", p);
while((p = strtok(NULL,"\n"))!=NULL)
{
printf("%s\n", p);
}
fclose(fptr);
return 0;
}
This is my code so far. When i compile it, it only shows the first line with number 7. How could i display all the lines? Thank you very much!
UPDATE of the code. Right now i can display the first and the second line but without number 2. I want to store the second line in v1 and the third line in v2.
if(fgets(buf,100,fptr)!=NULL)
p=strtok(buf,"\n");
printf("%s\n", p);
if((p = strtok(buf,"\n"))!=NULL && fgets(buf,100,fptr)!=NULL)
v1 = strtok(NULL,"\n");
printf("%s\n ",v1);
Here is the working code.
Cannot use char pointer for v1 and v2 since memory address of buffer is not constant. They need to be stored into arrays like you mentioned in the title but your description says the other thing. Need to skip lines with just newline characters.
#include <stdio.h>
#include <string.h>
int
main(void)
{
FILE *fp;
char caBuffer[50];
char caV1[50], caV2[50], caCnt[2];
const char *cp;
const char *cpDelimeter = "\n";
int iLoopCnt = 0;
if( ( fp = fopen( "xyz.txt", "r" ) ) == NULL ){
printf( "failed opening\n" );
return 1;
}
while( fgets( caBuffer, sizeof(caBuffer), fp ) != NULL ){
//skip the lines with newline char
if( strlen(caBuffer) > 1 )
{
cp = strtok(caBuffer, cpDelimeter);
if( cp != NULL ){
switch( iLoopCnt++ )
{
case 0:
strcpy(caCnt, cp );
break;
case 1:
strcpy(caV1, cp );
break;
case 2:
strcpy(caV2, cp );
break;
}
}
}
}
printf("caCnt = %s\n", caCnt );
printf("caV1 = %s\n", caV1 );
printf("caV2 = %s\n", caV2 );
fclose(fp);
return 0;
}
Updates were made per suggestion below.
Thanks.
So I've been trying to write a transposing algorithm where each character’s position is shifted to a new location within a file. For example if the key is 3 and the character array is
"This program is supposed to encrypt a file."
after encrypting, the output will be
"Tsrr ps cpai.h oaispetert lipgmsuodony fe"
The problem is that after it's done encrypting the first line of a file, it stops and doesn't continue encrypting the whole file. after compiling, it can be executed like this
./executable -e 3 inputfile outputfile
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 200
int main ( int argc, char* argv[ ] ) {
int pos = 0;
char characters[ BUFFER_SIZE ];
int index, k, size, key;
char* echars;
FILE* input;
FILE* output;
if ( argc == 5 ) {
// exits if key is lower than 1
key = atoi ( argv[ 2 ] );
if ( key < 1 ) {
perror ( "Error: This value cannot be used as a key" );
exit ( EXIT_FAILURE );
}
input = fopen ( argv[ 3 ], "r" );
//Shows error if there's no file
if ( input == NULL ) {
perror ( "Error, File doesn't exits" );
exit ( EXIT_FAILURE );
}
output = fopen ( argv[ 4 ], "w" );
fgets (characters, BUFFER_SIZE, input);
fseek (input, 0, SEEK_END);
size = ftell ( input );
echars = ( char* ) malloc ( size );
if ( strcmp ( argv[ 1 ], "-e" ) == 0 ) {
while (strlen(characters) && characters[strlen(characters)-1] == '\n'){
characters[strlen(characters)-1] = '\0';
for (index = 0; index < key; index++) {
for (k = index; k < strlen( characters ); k += key)
echars[ pos++ ] = characters[ k ];
}
printf("Successfuly encrypted\n");
}
} else if( strcmp ( argv[ 1 ], "-d" ) == 0 ) {
for (index = 0; index < key; index++) {
for (k = index; k < strlen( characters ); k += key)
echars[ k ] = characters[ pos++ ];
}
printf("Successfuly decrypted\n");
}
fputs( echars, output );
fclose ( input );
fclose ( output );
} else {
perror("Too few arguments, something went wrong\n");
printf("Usage: ./program -e (encrypts) or -d (decripts) 3 (key) inputfile destinationfile\n");
printf("Example:'./exectutable -e 3 inputfile.txt outputfile.txt\n");
exit ( EXIT_FAILURE );
}
return 0;
}
fgets stops reading when it hits a newline.
You need to either put a loop around it or use a read operation that can read larger chunks.