I'm new in C and try to do some practice
I want to change the txt file from
Chicken
10000
Cow
20000
Duck
20
Sheep
1000
to
Chicken 10000
Cow 20000
Duck 20
Sheep 1000
then destroy the animal <50
Chicken 10000
Cow 20000
Sheep 1000
My first step:
Read the file and compose the letter to the string.
Like the first word "Chicken", will be composed of "C" "h" "i" "c" "k" "e" "n".
As following my code, when I use strcpy(str0, ""); I have changed the string array strings[0] = str0; (str0 is "Chicken" now) before strcpy(str0, "");, but when strcpy(str0, "");operate, my memory in strings[0] was also be changed.
How can I fix the problem?
This is my code:
void append(char* s, char c)
{
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
}
int main() {
char str0[256] = "";
char tmp_char;
const char *string[2];
int i, c, line_counter=0;
FILE *file;
file = fopen("cry.txt", "r");
if (file) {
while ((c=getc(file)) !=EOF) {
if (c == 10) {
line_counter++;
string[0]=str0;
strcpy(str0, "");
continue;
}
tmp_char = c;
append(str0, tmp_char);
}
fclose(file);
}
return 0;
}
You should divide your problem into smaller pieces and implement them independently from each other --> "divide and conquer"
Before starting programming you should think about the steps.
I would analyze the problem the following way:
open infile
open outfile
while not eof
read data set --> 2 lines
read a line -> parse animal name
read a line -> parse number
filter data set
write data set
close files
I would derive the following structures/functions from this (or use library functions - depending on the class's task):
structure
DataSet{animalName, count};
function
readLine(filehandle, bufferpointer, maxbuffersize) -> success
readDataset(bufferpointer1, bufferpointer2) -> success
(parseAnimalName(linebuffer1, buffersize, namebuffer, maxlength) -> success)
(parseAnimalCount(linebuffer, numberpinter) -> success)
filterAnimal(DataSet) -> bool
writeAnimal(filehandle, DataSet) --> success
Depending on the possibility to use libraries parse functions I would omit the functions in parentheses.
With this isolated little functionalities it should be a lot easier to implement the whole problem and also analyze where bugs occur.
Once you solve the problem yourself you can compare it with my solution. I commented it pretty heavily.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LIMIT 30
// Arbitrary max number of items
#define MAX_ITEMS 16
// Arbitrary maximum result size
#define MAX_RESULT_SIZE 256
// Output string must keep both animal name and stringified integer.
// This integer will take at most 11 characters.
// It means that in string of format "%s %d\n" the animal name
// must take at most (MAX_RESULT_SIZE - 11 - whitespace - \n - NUL) characters.
#define MAX_STR_SIZE ((MAX_RESULT_SIZE) - 14)
int main(void) {
int retcode;
const char *filename = "file.txt";
FILE *file = fopen("file.txt", "r");
if (file == NULL) {
fprintf(stderr, "Failed to open file %s: %s", filename, strerror(errno));
}
char text[MAX_STR_SIZE + 1];
int number;
int id = 0;
char results[MAX_ITEMS][MAX_RESULT_SIZE];
// Dynamically define fmt string to limit fscanf to MAX_STR_SIZE
// Format specifier "%256s" makes sure that fscanf won't read a string that is
// longer than 256 characters (remember about additional one byte for NUL character,
// output memory must have space for 257 characters).
char fmt[32];
snprintf(fmt, sizeof(fmt), "%%%zus\n", (size_t)MAX_STR_SIZE);
while(1) {
if (id >= MAX_ITEMS) break;
retcode = fscanf(file, fmt, text);
if (retcode == EOF) break;
// From fscanf manual page we know 'On success, these functions return the
// number of input items successfully matched and assigned'. If this is
// different than 1 then something went wrong with input string. Maybe
// It's different than we assumed.
if (retcode != 1) {
fprintf(stderr, "Input is not matching format specifiers");
exit(EXIT_FAILURE);
}
retcode = fscanf(file, "%d\n", &number);
if (retcode == EOF) break;
if (retcode != 1) {
fprintf(stderr, "Input is not matching format specifiers");
exit(EXIT_FAILURE);
}
// Filtering logic
if (number < LIMIT) continue;
sprintf(results[id++], "%.*s %d", MAX_STR_SIZE, text, number);
}
for(int i = 0; i < id; i++) printf("%s\n", results[i]);
fclose(file);
return 0;
}
your line just assigns pointer to the variable str0 into string[0]
string[0]=str0;
That is why your string[0] changes after str0 changes. They point to the same memory.
To solve this, you have to copy value from str0 into string[0]
Related
First of all I am new to files in c, so it may be a simple question,
however I still didn't find a solution:
let's say that's the content of my file:
99
blah blah
...
...
I want to scan only the number from the beginning (it is always in a separate line)
My question is how to make it take the number (99) as one number and stop scanning.
int main(){
FILE* fp = fopen(file_name, "r");
int integer;
...
fclose(fp);
printf("%d", integer);
}
output for the file example:
99
-the nuber can be between 1 and 100-
I want to scan only the number from the beginning (it is always in a separate line).
That's a good hint, suggesting a line by line parsing of the input. You can use a combination of fgets(1) and sscan(2) to read that number.
fgets will read up to a certain number of character from a stream and store those character into a buffer. If it finds a newline, it stops reading, store the newline into the buffer followed by the null-terminator. Otherwise it only adds the terminator. If it fails, it returs a NULL pointer.
sscanf works basically like scanf or fscanf, but it reads from a character array, not from a stream.
It's also better to always check the return value of those library function.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SIZE 1024
int main(void)
{
char const *file_name = "data.txt";
FILE *in_file = fopen(file_name, "r");
if (!in_file) {
fprintf(stderr, "Error while reading \"%s\": %s", file_name, strerror(errno));
return EXIT_FAILURE;
}
char buffer[BUF_SIZE];
int number = 0;
while( fgets(buffer, BUF_SIZE, in_file) ) {
if ( sscanf(buffer, "%d", &number) == 1 ) {
if ( 0 < number && number < 100 ) {
printf("%d", number);
break;
}
}
}
fclose(in_file);
return EXIT_SUCCESS;
}
Example.
Some references of the functions used in the previous snippet
1) fgets: man-pages or cppreference.
2) sscanf: man-pages or cppreference
Why not use scanf? (fscanf to be more precise):
On success, the function returns the number of items of the argument
list successfully filled.
(source: cppreference)
So just check how many values did you read, if 0 that means it's not a number so you can just skip that string, for that you can use "%*" prefix to ignore the data.
You also said:
I want to scan only the number from the beginning (it is always in a
separate line)
so after you read the number just skip the whole line with "%*[^\n]" (reads
data until a new line symbol is encountered)
int num;
int scanReturn;
FILE* f = fopen("file.txt", "r");
...
do {
scanReturn = fscanf(f, "%d", &num);
if(scanReturn == 0)
{
scanReturn = fscanf(f, "%*s");
}
else if(scanReturn != EOF)
{
fscanf(f, "%*[^\n]");
printf("%d, ", num);
}
} while(scanReturn != EOF);
fclose(f);
I need to make program that checks if string inputted from console matches any string from input file and in my case it only works if i enter string that's last line in input file and i dont know why
int n;
char c[20];
char broj[20];
FILE* input;
input = fopen("input.txt", "r");
scanf("%s", broj);
while(fgets(c, 200, input) != NULL)
{
if(strcmp(c, broj) == 0)
printf("%s", c);
}
printf("\n");
fclose(input);
return 0;
As some have pointed out, you are reading too much into your buffer.
I really do not like to see the use of sizeof operator when calculating buffer sizes as the results can change depending on context.
void printSizeofTest1(char *test1) {
printf("Size of test1: %d\n", sizeof(test1));
}
int main() {
char *test = NULL;
char test1[10] = { 0 };
char test2 = '\0';
printf("Size of test: %d\n", sizeof(test));
printf("Size of test1: %d\n", sizeof(test1));
printf("Size of test2: %d\n", sizeof(test2));
printSizeofTest1(test1);
return 0;
}
Size of test: 4
Size of test1: 10
Size of test1: 1
Size of test1: 4
And you see this often when copying and pasting code.
Instead, it's far better to define the length of your pointers as macro expressions and always add a NULL byte pad for signed chars. And never reference it via sizeof but via the macro. This also means that if you need to change the size of the buffer, you only need to change it in one place.
With regards to your question. It's difficult without seeing the input file, however, when you use fgets it's going to pull back any new line ending characters, which may not necessary represent your input.
#include <stdio.h>
#include <string.h>
#define BUFF_SIZE 20
int main() {
char c[BUFF_SIZE+1] = { 0 },
broj[BUFF_SIZE+1] = { 0 };
FILE *input = fopen("input.txt", "r");
if(NULL != input) { /* <- Test for NULL file pointer */
scanf("%20s", broj); /* <- Provide a width specifier to avoid buffer overflow */
while(fgets(c, BUFF_SIZE, input) != NULL) {
printf("Searching for: %s\nFound: %s\n", broj, c);
if(strcmp(c, broj) == 0)
printf("%s", c);
memset(c, '\0', BUFF_SIZE); /* <- Clear buffer to ensure consistent search results */
}
fclose(input);
input = NULL; /* <- Assign to NULL so that you can check to see if it's closed */
}
printf("\n");
return 0;
}
In this example, I never find the contents of my file because my search is looking for a new line character which does not exist in my search string.
Instead, you should:
Remove the new line encodings from the file
Ignore the new line encodings
Search for precisely what you are looking for
So basically the text file would look like this
Starting Cash: 1500
Turn Limit (-1 for no turn limit): 10
Number of Players Left To End Game: 1
Property Set Multiplier: 2
Number of Houses Before Hotels: 4
Must Build Houses Evenly: Yes
Put Money In Free Parking: No
Auction Properties: No
Salary Multiplier For Landing On Go: 1
All I need from the file is basically anything after ":"
I'm just confused how to only read anything after a ":"?
This is what I have right now. I just can't seem to think of a way to only scan for the numbers/yesorno.
void readRules(char*file_name)
{
Rules r;
FILE *file = NULL;
file = fopen(file_name, "r");
if (file == NULL) {
printf("Could not open %s\n", file_name);
return;
}
char c=fgetc(file);
fscanf(file, "%c", &c);
while (!feof(file))
{
fscanf(file, "%c", &c);
if(c==':')
{
r.startCash=c;
}
}
printf("There are %c word(s).\n", r.startCash);
fclose(file);
}
Thank you.
This program will read integers following a colon in each line of the file given. I imagine this is appropriate? You also have some strings after colons. If you want to read those, you can try scanning for a string "%s" and testing if the function returns nonzero (for at least one format pattern matched).
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 1000
void readRules (const char *filename) {
FILE *fp;
char *lp, line[MAXLINE];
int n;
// Return early if file cannot be opened.
if ((fp = fopen(filename, "r")) == NULL) {
fprintf(stderr, "Error: Couldn't open \"%s\"!\n", filename);
return;
}
// Use fgets to read consecutive lines. Returns NULL on error or EOF.
while (fgets(line, MAXLINE, fp) != NULL) {
// Read until newline is hit or buffer size exceeded.
for (lp = line; *lp != '\n' && (lp - line) < MAXLINE; lp++) {
// If encounter colon and sccanf reads at least 1 integer..
if (*lp == ':' && sscanf(lp + 1, "%d", &n) == 1) {
fprintf(stdout, "%d\n", n);
break;
}
}
}
// Clean up.
fclose(fp);
}
int main (int argc, const char *argv[]) {
readRules("test.txt");
return 0;
}
When run with your example input, it produces:
1500
10
1
2
4
1
I have created a function that takes as a parameter the name of a source file, the name of a destination file and the beginning and end lines of the source file lines that will be copied to the destination file, like the example below. All I want to do is to input the lines that I want to copy to the other text file like the example below:
The code I show you just "reads" the content of the one text file and "writes" another one. I want to "write" specific lines that the user gives, not the whole text file
Inputs by the user:
Source_file.txt //the file that the destination file will read from
destination_file.txt //the new file that the program has written
2 3 // the lines that it will print to the destination file: 2-3
Source_file.txt:
1
2
3
4
5
6
destination_file.txt
2
3
code:
#include <stdio.h>
#include <stdlib.h>
void cp(char source_file[], char destination_file[], int lines_copy) {
char ch;
FILE *source, *destination;
source = fopen(source_file, "r");
if (source == NULL) {
printf("File name not found, make sure the source file exists and is ending at .txt\n");
exit(EXIT_FAILURE);
}
destination = fopen(destination_file, "w");
if (destination == NULL) {
fclose(source);
printf("Press any key to exit...\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(source)) != EOF)
fputc(ch, destination);
printf("Copied lines %d from %s to %s \n",
lines_copy, source_file, destination_file, ".txt");
fclose(source);
fclose(destination);
}
int main() {
char s[20];
char d[20];
int lines;
printf("-Enter the name of the source file ending in .txt\n"
"-Enter the name of the destination file ending in .txt\n"
"-Enter the number of lines you want to copy\n\n");
printf(">subcopy.o ");
gets(s);
printf("destination file-> ");
gets(d);
printf("Lines: ");
scanf("%d", &lines);
cp(s, d, lines);
return 0;
}
In cp(), in order to select the lines to keep, you have to know their position in the input-file. Thus, you need to count lines.
Using fgets instead of fgetc will allow you to count the lines.
On the other hand, if I wanted to select lines 3 and 7 to 12 in a file, I'd use:
sed -n -e "3p;7,12p" < input.txt > output.txt
this is a very simple solution, let's say you know that the maximun length of a line will be 100 characters for simplicity (if a line is longer than 100 characters only the first 100 will be taken)
at the top (outside main) you can write
#ifndef MAX_LINE_SIZE
#define MAX_LINE_SIZE 100
#endif
i know many people don't like this but i think in this case it makes the code more elegant and easier to change if you need to modify the maximum line size.
to print only the wanted lines you can do something like this
char line[MAX_LINE_SIZE];
int count = 0;
while (fgets(line, MAX_LINE_SIZE, source)){
count++;
if (3 <= count && count <= 5){
fputs(line, destination);
}
}
The while loop will end when EOF is reched because fgets returns NULL.
P.S. there could be some slight errors here and there since i wrote it pretty fast and going by memory but in general it should work.
There are some problems in your program:
Do not use gets(), it may cause buffer overflows.
Always use type int to store the return value of fgetc() in order to distinguish EOF from regular byte values.
You pass an extra argument ".txt" to printf(). It will be ignored but should be removed nonetheless.
To copy a range of lines from source to destination, you can just modify your function this way:
#include <stdio.h>
#include <string.h>
#include <errno.h>
void cp(char source_file[], char destination_file[], int start_line, int end_line) {
int ch;
int line = 1, lines_copied;
FILE *source, *destination;
source = fopen(source_file, "r");
if (source == NULL) {
printf("Cannot open input file %s: %s\n",
source_file, strerror(errno));
exit(EXIT_FAILURE);
}
destination = fopen(destination_file, "w");
if (destination == NULL) {
printf("Cannot open output file %s: %s\n",
destination_file, strerror(errno));
fclose(source);
exit(EXIT_FAILURE);
}
while ((ch = fgetc(source)) != EOF) {
if (line >= start_line && line <= end_line) {
fputc(ch, destination);
}
if (ch == '\n') {
line++;
}
}
lines_copied = 0;
if (line > start_line) {
if (line >= end_line) {
lines_copied = end_line - start_line + 1;
} else {
lines_copied = line - start_line + 1;
}
}
printf("Copied lines %d from %s to %s\n",
lines_copy, source_file, destination_file);
fclose(source);
fclose(destination);
}
int main() {
char source_file[80];
char destination_file[80];
int start_line, end_line;
printf("-Enter the name of the source file ending in .txt\n"
"-Enter the name of the destination file ending in .txt\n"
"-Enter the start and end line\n\n");
printf(">subcopy.o ");
if (scanf("%79s", source_file) != 1) {
return 1;
}
printf("destination file-> ");
if (scanf("%79s", destination_file) != 1) {
return 1;
}
printf("Start and end lines: ");
if (scanf("%d %d", &start_line, &end_line) != 2) {
return 1;
}
cp(source_file, destination_file, start_line, end_line);
return 0;
}
I am a biology student and I am trying to learn perl, python and C and also use the scripts in my work. So, I have a file as follows:
>sequence1
ATCGATCGATCG
>sequence2
AAAATTTT
>sequence3
CCCCGGGG
The output should look like this, that is the name of each sequence and the count of characters in each line and printing the total number of sequences in the end of the file.
sequence1 12
sequence2 8
sequence3 8
Total number of sequences = 3
I could make the perl and python scripts work, this is the python script as an example:
#!/usr/bin/python
import sys
my_file = open(sys.argv[1]) #open the file
my_output = open(sys.argv[2], "w") #open output file
total_sequence_counts = 0
for line in my_file:
if line.startswith(">"):
sequence_name = line.rstrip('\n').replace(">","")
total_sequence_counts += 1
continue
dna_length = len(line.rstrip('\n'))
my_output.write(sequence_name + " " + str(dna_length) + '\n')
my_output.write("Total number of sequences = " + str(total_sequence_counts) + '\n')
Now, I want to write the same script in C, this is what I have achieved so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
input = FILE *fopen(const char *filename, "r");
output = FILE *fopen(const char *filename, "w");
double total_sequence_counts = 0;
char sequence_name[];
char line [4095]; // set a temporary line length
char buffer = (char *) malloc (sizeof(line) +1); // allocate some memory
while (fgets(line, sizeof(line), filename) != NULL) { // read until new line character is not found in line
buffer = realloc(*buffer, strlen(line) + strlen(buffer) + 1); // realloc buffer to adjust buffer size
if (buffer == NULL) { // print error message if memory allocation fails
printf("\n Memory error");
return 0;
}
if (line[0] == ">") {
sequence_name = strcpy(sequence_name, &line[1]);
total_sequence_counts += 1
}
else {
double length = strlen(line);
fprintf(output, "%s \t %ld", sequence_name, length);
}
fprintf(output, "%s \t %ld", "Total number of sequences = ", total_sequence_counts);
}
int fclose(FILE *input); // when you are done working with a file, you should close it using this function.
return 0;
int fclose(FILE *output);
return 0;
}
But this code, of course is full of mistakes, my problem is that despite studying a lot, I still can't properly understand and use the memory allocation and pointers so I know I especially have mistakes in that part. It would be great if you could comment on my code and see how it can turn into a script that actually work. By the way, in my actual data, the length of each line is not defined so I need to use malloc and realloc for that purpose.
For a simple program like this, where you look at short lines one at a time, you shouldn't worry about dynamic memory allocation. It is probably good enough to use local buffers of a reasonable size.
Another thing is that C isn't particularly suited for quick-and-dirty string processing. For example, there isn't a strstrip function in the standard library. You usually end up implementing such behaviour yourself.
An example implementation looks like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXLEN 80 /* Maximum line length, including null terminator */
int main(int argc, char *argv[])
{
FILE *in;
FILE *out;
char line[MAXLEN]; /* Current line buffer */
char ref[MAXLEN] = ""; /* Sequence reference buffer */
int nseq = 0; /* Sequence counter */
if (argc != 3) {
fprintf(stderr, "Usage: %s infile outfile\n", argv[0]);
exit(1);
}
in = fopen(argv[1], "r");
if (in == NULL) {
fprintf(stderr, "Couldn't open %s.\n", argv[1]);
exit(1);
}
out = fopen(argv[2], "w");
if (in == NULL) {
fprintf(stderr, "Couldn't open %s for writing.\n", argv[2]);
exit(1);
}
while (fgets(line, sizeof(line), in)) {
int len = strlen(line);
/* Strip whitespace from end */
while (len > 0 && isspace(line[len - 1])) len--;
line[len] = '\0';
if (line[0] == '>') {
/* First char is '>': copy from second char in line */
strcpy(ref, line + 1);
} else {
/* Other lines are sequences */
fprintf(out, "%s: %d\n", ref, len);
nseq++;
}
}
fprintf(out, "Total number of sequences. %d\n", nseq);
fclose(in);
fclose(out);
return 0;
}
A lot of code is about enforcing arguments and opening and closing files. (You could cut out a lot of code if you used stdin and stdout with file redirections.)
The core is the big while loop. Things to note:
fgets returns NULL on error or when the end of file is reached.
The first lines determine the length of the line and then remove white-space from the end.
It is not enough to decrement length, at the end the stripped string must be terminated with the null character '\0'
When you check the first character in the line, you should check against a char, not a string. In C, single and double quotes are not interchangeable. ">" is a string literal of two characters, '>' and the terminating '\0'.
When dealing with countable entities like chars in a string, use integer types, not floating-point numbers. (I've used (signed) int here, but because there can't be a negative number of chars in a line, it might have been better to have used an unsigned type.)
The notation line + 1 is equivalent to &line[1].
The code I've shown doesn't check that there is always one reference per sequence. I'll leave this as exercide to the reader.
For a beginner, this can be quite a lot to keep track of. For small text-processing tasks like yours, Python and Perl are definitely better suited.
Edit: The solution above won't work for long sequences; it is restricted to MAXLEN characters. But you don't need dynamic allocation if you only need the length, not the contents of the sequences.
Here's an updated version that doesn't read lines, but read characters instead. In '>' context, it stored the reference. Otherwise it just keeps a count:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h> /* for isspace() */
#define MAXLEN 80 /* Maximum line length, including null terminator */
int main(int argc, char *argv[])
{
FILE *in;
FILE *out;
int nseq = 0; /* Sequence counter */
char ref[MAXLEN]; /* Reference name */
in = fopen(argv[1], "r");
out = fopen(argv[2], "w");
/* Snip: Argument and file checking as above */
while (1) {
int c = getc(in);
if (c == EOF) break;
if (c == '>') {
int n = 0;
c = fgetc(in);
while (c != EOF && c != '\n') {
if (n < sizeof(ref) - 1) ref[n++] = c;
c = fgetc(in);
}
ref[n] = '\0';
} else {
int len = 0;
int n = 0;
while (c != EOF && c != '\n') {
n++;
if (!isspace(c)) len = n;
c = fgetc(in);
}
fprintf(out, "%s: %d\n", ref, len);
nseq++;
}
}
fprintf(out, "Total number of sequences. %d\n", nseq);
fclose(in);
fclose(out);
return 0;
}
Notes:
fgetc reads a single byte from a file and returns this byte or EOF when the file has ended. In this implementation, that's the only reading function used.
Storing a reference string is implemented via fgetc here too. You could probably use fgets after skipping the initial angle bracket, too.
The counting just reads bytes without storing them. n is the total count, len is the count up to the last non-space. (Your lines probably consist only of ACGT without any trailing space, so you could skip the test for space and use n instead of len.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
FILE *my_file = fopen(argv[1], "r");
FILE *my_output = fopen(argv[2], "w");
int total_sequence_coutns = 0;
char *sequence_name;
int dna_length;
char *line = NULL;
size_t size = 0;
while(-1 != getline(&line, &size, my_file)){
if(line[0] == '>'){
sequence_name = strdup(strtok(line, ">\n"));
total_sequence_coutns +=1;
continue;
}
dna_length = strlen(strtok(line, "\n"));
fprintf(my_output, "%s %d\n", sequence_name, dna_length);
free(sequence_name);
}
fprintf(my_output, "Total number of sequences = %d\n", total_sequence_coutns);
fclose(my_file);
fclose(my_output);
free(line);
return (0);
}