I currently have a shell script in C that takes a command of a file, two numbers, mth line and nth line, and supposedly should output whats in between those lines:
for example:
./output file1 2 6
would output file from line 2 to line 6
I have it implemented currently in a way of outputting the whole file, I have been trying to change it to output specifically between those lines
this is my code
#include <fcntl.h>
int main(int argc, char *argv[])
{
int file;
int size;
int l[1000];
int firstNum = atoi(argv[2]);
int secondNum = atoi(argv[3]);
file = open(argv[1], O_RDONLY);
if( file == -1)
{
printf("\n cannot open file \n");
}
while ((size=read(file,l,80)) > 0)
write(1,l,size);
}
I tried to change l and size to firstNum and secondNum, which are the numbers entered from the command line, but still did not work and outputted one single line.
What is a better way of doing so ?
There are several issues with your code, so just go through them sequentially:
It's better to use high level fopen rather than low level open to open a file. So it's better to write this way:
FILE *file = fopen(argv[1], "r");
if (file == NULL)
exit(EXIT_FAILURE);
Your read is wrong as it reads exactly 80 characters instead of a line as you expect.
while ((size=read(file,l,80)) > 0) // <-- WRONG because this reads 80 chars instead of one line
For similar reason as with open, it's better to use alternative like printf instead of low level read and write.
To read line by line, you should use library function getline.
To control what line number to print, a simple way is to have a variable tracking what line number and compare with your command line arguments.
So put them together, you would need something like this:
FILE *file = fopen(argv[1], "r");
if (file == NULL)
exit(EXIT_FAILURE);
int line_num = 0;
char * line = NULL;
size_t len = 0;
ssize_t read = 0;
while ((read = getline(&line, &len, file)) != -1)
{
line_num++;
if( firstNum <= line_num && line_num <= secondNum )
{
printf( "line %d: %s", line_num, line );
if( line_num == secondNum )
break;
}
}
Try looking at this post: C read file line by line
From there it should be a simple matter of counting lines read and discarding them until you have read firstNum lines at which point print until you've reached secondNum.
Related
I'm new to programming in C. And I'm trying to print the first 10 lines of a text file. When I run my program with a text file containing 11 lines of text, only the first line is displayed. I'm not sure why it does that, but I suspect there is something wrong in my while loop. Can someone please help me?
#include <stdio.h>
int main(int argc, char *argv[]){
FILE *myfile;
char content;
int max = 0;
// Open file
myfile = fopen(argv[1], "r");
if (myfile == NULL){
printf("Cannot open file \n");
exit(0);
}
// Read the first 10 lines from file
content = fgetc(myfile);
while (content != EOF){
max++;
if (max > 10)
break;
printf ("%c", content);
content = fgetc(myfile);
}
fclose(myfile);
return 0;
}
You have been already advised to use fgets. However, if your file has lines of unknown length, you may still want to use fgetc. Just make sure you count only newlines, not all characters:
int max = 0;
int content;
while ((content = fgetc(myfile)) != EOF && max < 10){
if (content == '\n') max++;
putchar(content);
}
fgetc() returns the next character in the file, not the next line. You probably want to use fgets() instead, which reads up to the next newline character into a buffer. Your code should probably end up with something like:
// allocate 1K for a buffer to read
char *buff = malloc(1024);
// iterate through file until we are out of data or we read 10 lines
while(fgets(buff, 1024, myfile) != NULL && max++ < 10) {
printf("%s\n", buff);
}
free(buff);
// close your file, finish up...
Read more about fgets() here: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
fgetc function reads the next character not the next ine. for reading the number of lines you should use fgets function. this function reads the full string till the end of the one line and stores it in a string.
your code Shuld be as:-
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *myfile;
char content[200];
int max = 0;
// Open file
myfile = fopen(argv[1], "r");
if (myfile == NULL)
{
printf("Cannot open file \n");
exit(0);
}
// Read the first 10 lines from file
fgets(content, 200, myfile);
while (content != EOF)
{
max++;
if (max > 10)
break;
printf("%s", content);
fgets(content, 200, myfile);
}
fclose(myfile);
return 0;
}
So both the code to print the lines from the txt file and the code to count the lines in the txt file work fine when the other is commented out but when i try to have both work only the code that comes first works e.g. if i put the code to print out the lines first, the line count is always zero. However if i put the code to count the lines first, the number is correct but the lines from the txt file are not printed :S
#include <stdio.h>
int main(int argc, char const *argv[])
{
const int SIZE = 128;
char line[SIZE];
FILE *srcFile;
int c;
int count = 0; // Line counter (result)
if (argc == 1)
{
printf("No command line arguments given!\n");
return 1;
}
srcFile = fopen(argv[1], "r");
if (srcFile == NULL)
{
perror("\n*** FILE OPEN FAILED ***");
}
else
{
printf("\n*** FILE OPEN SUCCESSFUL ***\n\n");
}
while(fgets(line, SIZE, srcFile) != NULL)
{
printf("%s", line);
}
for (c = fgetc(srcFile); c != EOF; c = fgetc(srcFile))
{
if (c == '\n')
{
count ++;
}
}
if(c != '\n' && count != 0)
{
count ++;
}
printf("The file %s has %d lines\n ", argv[1], count);
fclose(srcFile);
return 0;
}
Here is a quick overview of how working with files is done in most programming languages:
When you open a file in a program you obtain a handle to that file. What the handle representation is depends on the language. In c it is the FILE structure. The handle contains - among other things - a file position indicator. Every read and write to that file through this handle happens at that position. Usually a read/write operation advances this file position indicator. Think about it: how do consecutive reads know to each read where the previous one left? You don't provide an argument to the read function telling it where to read from. You just "say" read. What happens is that each read call reads at the file position indicator and then advances this indicator, thus when the next read happens the handle has an updated file position indicator.
So the solution to your problem is - as mentioned in the comments - to put this position indicator to the beginning of the file. In c this can be done with rewind.
curious how the code fragment would have to look to include the line
count int the same loop as the print lines
Simple. Pseudocode:
line_count = 0
while (read line successful)
{
print line
increment line_count
}
print line_count
I am trying to make a method that takes as a parameter the number of a line and the file in which I want the data
char SearchLine(int numberLine,char[100] fileName)
{
char line[256];
int nline=numberLine;
int i=0;
FILE *ifp;
ifp = fopen(fileName, "r");
while (fscanf(ifp, "%s", &line) == 1)
{
if(i==nline)
{
printf("\n%d",i);
}
else if(i<nline)
{
i++;
}
}
printf("\n%s",line);
printf("\n%s\n\n",i);
system("pause");
return line;
}
You haven't been particularly clear about what you are trying to do, but to write to a specific line, just move to the start of that line (either by reading each line until you reach the one you want or by using seek()), and then write your data.
The problem is that typical text files have lines of different lengths. Therefore, if the data you write to that line is more or less than the data that was on the line before, then you'll mess up your data.
The only way to make this work reliable is:
Require that every line in your file is always a known, fixed length.
Or copy the entire file, writing the new line as you write than line to the new file.
If I understand you want a function that you can pass a filename and a numberLine to as arguments and then have the function return that line for editing, then you can use something like the following:
#include <stdio.h>
#include <stdlib.h>
#define MAXC 256
char *SearchLine (int numberLine, char *line, char *filename);
int main (int argc, char **argv) {
if (argc < 2) {
fprintf (stderr, "error: insufficient input. usage: %s filename.\n",
argv[0]);
return 1;
}
char linebuf[MAXC] = {0};
int linenum = argc > 2 ? atoi (argv[2]) : 3;
SearchLine (linenum, linebuf, argv[1]);
printf ("\n line[%3d] : %s\n", linenum, linebuf);
return 0;
}
/* read line number 'numberLine' from 'filename' into 'line'.
* Returns line on success, NULL otherwise. (numberline is zero based)
*/
char *SearchLine (int numberLine, char *line, char *filename)
{
int index = 0;
FILE *fp = fopen (filename, "r");
if (!fp) {
fprintf (stderr, "SearchLine() error: file open failed '%s'\n",
filename);
return NULL;
}
while (fgets (line, MAXC, fp)) {
if (index == numberLine)
return line;
if (index > numberLine)
break;
index++;
}
fclose (fp);
*line = 0;
return NULL;
}
Note: the line numbers are zero based (e.g. 0, 1, ...), adjust if you want them to run from 1.
Looking at SearchLine note an array to hold the line is passed as one of the arguments. Line has MAXC characters (adjust as required). By passing an array to the function, you eliminate the need to allocate space in SearchLine. The function simply reads each line in the file until the counter index == numberLine and then returns the line. If if the number given is greater than the number of lines in the file, then line is set to the empty-string and NULL is returned.
A quick example would be:
Input File
$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Output
$ ./bin/fgets_linenum dat/captnjack.txt 1
line[ 1] : Of Captain Jack Sparrow
$ ./bin/fgets_linenum dat/captnjack.txt 4
line[ 4] :
If you were looking to handle/edit the line in a different manner, let me know and I'm happy to help further.
Adding to Jonathan's answer.
To write to a file, one must open the file in read/write mode, i.e.,
ifp = fopen(filename, "r+");
//"r", as in your case, it opens the file in read-only mode.
This would open the file in read/write mode provided that the file exists. As a good programming practice, always check if the file is existing: if(ifp != NULL) {}.
One major thing is that you SHOULD always close the file that you've opened. fclose(ifp);
I have a project I am working on, basically it requires me to read switches from the command line and reads and writes some output to the same file overwriting its previous content. If the file is not specified it reads from stdin and writes to stdout, here is a function to number all output lines:
int main(int argc,char *argv[]) {
FILE *fp;
if((fp = fopen(argv[2]),"a+")) == 0) {
fp = stdin; /* if we cant read the file then it is read from stdin*/
}
if((strcmp("-e",argv[1])) == 0){
if(fp == stdin) {
display_dollar(fp);
}
}
fclose(fp);
}
and this is my display_dollar function (the one that actually prints to stdout or fp)
void display_dollar(FILE *fp) {
char line[LINESIZE];
char lines[LINESIZE];
while(fgets(line,LINESIZE,fp)) {
sscanf(line,"%s\n",lines);
if(fp == stdin) {
printf("%s%c\n",lines,'$');
lines[0] = '\0'; /* "clear" buffer */
} else {
fprintf(fp,"%s%c\n",lines,'$');
lines[0] = '\0'; /* "clear" buffer */
}
}
}
This works perfectly for stdout but I am having trouble printing anything to the file specified by fp, I dont understand what I am doing wrong. Thanks!
Some suggested i take out the parantheses when I compare fp but I get an compiler warning when I compile it with
gcc -ansi -W -Wall -pedantic
The warning as follows:
assign2.c: In function ‘main’:
assign2.c:20:9: warning: assignment makes pointer from integer without a cast [enabled by default]
if(fp = fopen(argv[argc - 1],"a+") == 0) {
^
assign2.c:20:3: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
if(fp = fopen(argv[argc - 1],"a+") == 0) {
^
Original code
Look very carefully at this line — it doesn't do what you want:
if((fp = fopen(argv[2]),"a+") == 0) {
^ ^ ^ ^
1 2 2 1
The parentheses aren't paired up as you intend. You're not passing enough arguments to fopen(). And you're comparing "a+" with 0.
You really want:
if ((fp = fopen(argv[2], "a+")) == 0) {
Revised code
In the revised code, you seem to have:
if(fp = fopen(argv[argc - 1],"a+") == 0) {
This is parsed as:
if (fp = (fopen(argv[argc - 1], "a+") == 0)) {
It is assigning the 0 or 1 result of comparing the return value from fopen() with 0, and that isn't a pointer, hence the complaint about assignment makes pointer from integer without a cast.
The second warning about suggest parentheses around assignment used as truth value is indicative of the problem; the result of the assignment is used as the condition, which is only sometimes correct (and this is one of the times when it isn't). It more usually appears in a context such as:
if (i = j)
where you probably intended:
if (i == j)
but if you really wanted to test the assignment, then you should write:
if ((i = j) != 0)
or (perish the thought — I hate it when people do this!):
if ((i = j))
Still having problems…
but it still won't write to the file,
You have a single read/write position in a file. You are reading from and writing to the same file, fp. If that is stdin, you've go one set of problems; if it is a regular text file, you have another set.
First of all, between a read and a write (or a write and a read) on a file opened for update, you must do a positioning operation — fseek() or rewind(), mainly. Secondly, because you opened the file in append mode, all writes occur at the end of the file. That means that when you try to read after a write, the position is at the end of the file (aka EOF).
If the file is standard input, that is often not seekable, which has implications, too. Standard input (or, at least, the underlying file descriptor 0 on Unix) is very often writable, but that's not something you're supposed (ab)use like that.
You either need to have an input file and an output file, or you need to be seeking appropriately.
And maybe you want "r+" instead of "a+" mode.
this code:
void display_dollar(FILE *fp) {
char line[LINESIZE];
char lines[LINESIZE];
while(fgets(line,LINESIZE,fp)) {
sscanf(line,"%s\n",lines);
if(fp == stdin) {
printf("%s%c\n",lines,'$');
lines[0] = '\0'; /* "clear" buffer */
} else {
fprintf(fp,"%s%c\n",lines,'$');
lines[0] = '\0'; /* "clear" buffer */
}
}
}
1) changes the number of characters in the next line, so the output file will be corrupted.
2) each read/write to the file moves the file-position-pointer.
you could use ftell() to see this happening.
3) the code must not corrupt the file, so it will have to write a new file.
4) this has a logic error, the %s will stop when any white space is encountered.
This includes newlines (of which there will (almost) always be one
or any tab or any space.
then appending a '$' will overlay something in the line.
therefore, the line to output will be corrupted.
the corruption could be a space or a NUL or a tab or a newline being overlayed
Suggestions:
1) When outputting to a file, always create a new file
2) always find the newline with strstr() (or similar), overlay the newline with "$\0",
then strcat() "\n\0"
this will mean modifying the display_dollar() declaration to include both the input and output file pointers
Alright, long story short I rewrote the code to have an input and output file, because things would get more complicated overwriting the current file in place without using a buffer.
Please let me know if there are any issues, or if things need to be explained.
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define LINESIZE 1024
void display_dollar(FILE *, FILE *);
int main(int argc, char *argv[]) {
FILE *infp = stdin;
FILE *outfp = stdout;
for (int i = 4; i < argc; ++i)
fprintf(stderr, "Unknown argument: %s\n", argv[i]);
if (argc >= 4 && (outfp = fopen(argv[3], "w")) == NULL)
outfp = stdout;
if (argc >= 3 && (infp = fopen(argv[2], "r")) == NULL)
infp = stdin;
if (argc >= 2 && strcmp("-e", argv[1]) == 0)
display_dollar(infp, outfp);
else
fprintf(stderr, "Usage: %s -e [infile [outfile]]\n", argc ? argv[0] : "program");
fclose(infp);
fclose(outfp);
return 0;
}
void remove_newlines(char *str) {
for(long i = strlen(str)-1; i >= 0 && (str[i] == '\n' || str[i] == '\r'); --i)
str[i] = '\0';
}
void display_dollar(FILE *infp, FILE *outfp) {
char line[LINESIZE];
while (fgets(line, LINESIZE, infp)) {
remove_newlines(line);
fprintf(outfp, "%s%c\n", line, '$');
}
}
Code:
#define maxWords 200
//finput is the file
char tempWord[maxWords];
(for i = 0; i < lineCounter(f); i++)
{
fgets(tempWord, maxWords, finput);
printf("%s", tempWord);
}
The lineCounter function works fine and outputs the right amount of lines in the text file. But for some reason it prints only 150 lines out of 1500 lines which I don't understand. I've been trying other functions such as fscanf and others and I still have the same issue. They all print out text but not the WHOLE text file.
Even if I put i < 1500 as the condition in the for-loop I still have this problem. Does anyone know why? Also I tried in while-loop form too but no luck.
Also I know there are many topics related to reading a text file, I have read them but I still have this problem..
This small program should print out the line number and the line itself of an entire text file.
#define INPUT_FILE 1
#define LINE_LEN 200
int main(int argc, char *argv[]) {
int line_num;
char string[LINE_LEN];
FILE *file;
if((file = fopen(argv[INPUT_FILE], "r")) == NULL) {
fprintf(stderr, "Could not open input file\n");
exit(1);
}
line_num = 1;
while( fgets(string, LINE_LEN, file) != NULL ) {
line_num++
printf( "Line %d: <%s>\n", line_num, string);
}
return 0;
}
EDIT:
Initialized line_num to 1 and added the tags as suggested by #chux