Can any one explain to me how to make a C program to read input from a file according to the following scenario:
12
2-4,7,9;
1,4,11-12;
1,4,10,12;
1,4-8,10-12;
1,8;
1,3-6,8,10-12;
1,3,5-6,8,11;
1,8,10-12;
1-8;
;
2;
2-4,7-10,12;
The first number (on the first line) describes what size the grid should be, in this case a 12x12 grid. The following lines describe how many cells are occupied on each row of the grid. For example, in the first row the cells from 2 to 4 and 7 and 9 are occupied; in the second row, the cells 1, 4 and from 11 to 12 are occupied and so on.
Right now I have this code, but it is not solving my problem ...
#include <stdio.h>
#include <stdlib.h>
void main()
{
char content[3000];
int value;
FILE *ptr_file = fopen("data.txt", "r");
if(!ptr_file)
return 1;
int j;
while(fgets(content, 3000, ptr_file)!=NULL){
printf("%s", content);
value = atoi(content);
for(j=0; j<3000; j++){
value = content[j];
printf("%i", value);
}
}
return 0;
}
Console throws just a bunch of random numbers ...
Pseudo-code:
Open your file
Read the first line
Extract the value N
Allocate your grid
Loop N times
Read a line
If not an empty line, ie. semi-colon only
Split into tokens by comma
Check for a range or a single digit
Extract numbers
Set cells accordingly
The "random" numbers are the byte values from your file and you forgot to stop at end of line.
Dave's solution is not quite right for c.
After read a line:
while not semicolon
strtoul a number
if no number
exit error
if next char is hyphen
shift to next char
strtoul end of range
if no number
exit error
set cells
else
set cell
if next char is not semicolon
shift to next char
You should not use atoi for anything ever. Use sscanf or strto….
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
enum { EOD = -1, EOL = -2, ERR = -3, OFF = 0, ON = 1 };//End Of Data, End Of Line
int getValue(FILE *fp){
static int on_yield = OFF, prev, end;
if(on_yield == ON && prev < end){
return ++prev;
}
on_yield = OFF;
int n, ch = fgetc(fp);
if(ch == EOF)
return EOD;
else if(ch == ';')
return EOL;
else if(ch == ',' || ch == '\n')
return getValue(fp);//or change to using loop
else if(isdigit(ch)){
ungetc(ch, fp);
fscanf(fp, "%d", &n);
return prev=n;
} else if(ch == '-'){
on_yield = ON;
fscanf(fp, "%d", &n);
end = n;
return getValue(fp);
}
fprintf(stderr, "(%c) invalid format in file\n", ch);
return ERR;
}
int main(void){
FILE *ptr_file = fopen("data.txt", "r");
if(!ptr_file){
perror("fopen");
return 1;
}
int size;
fscanf(ptr_file, "%d", &size);//check omitted
char (*cell)[size] = malloc(size * sizeof(*cell));
memset(&cell[0][0], ' ', size*size);
int r = 0, c, value;
while((value=getValue(ptr_file))!=EOD){
if(value == EOL){
++r;
continue;
}
if(value > 0)
cell[r][value-1] = '*';
}
fclose(ptr_file);
for(r = 0; r < size; ++r){
for(c = 0; c < size; ++c){
putchar(cell[r][c]);
}
putchar('\n');
}
free(cell);
return 0;
}
Related
I have to read from a txt file these lines:
1 334.5909245845 161.7809319139
2 397.6446634067 262.8165330708
3 503.8741827107 172.8741151168
4 444.0479403502 384.6491809647
5 311.6137146746 2.0091699828
6 662.8551011379 549.2301263653
7 40.0979030612 187.2375430791
From here I have to extract the second and third values of each line because they're the coordinates for my cities.
My piece of code is the following one (I will show only the part where the program has to read the values):
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdlib.h>
struct city {
double x;
double y;
};
int main(int argc, const char *argv[])
{
FILE *f;
f = fopen(argv[1], "r");
if (f == NULL)
{
printf("cannot read file or inexistant file.\n");
return 1;
}
char lines[2000][150];
int i = 0;
int j = 0;
int c = 0;
// read file's lines
while (c != EOF)
{
while ((c = getc(f)) != EOF && c != '\n')
{
lines[i][j] = c;
j++;
}
lines[i][j] = '\n';
i++;
j = 0;
}
strcpy(lines[i - 2], "\n");
struct city * cities = malloc(sizeof(double) * 10 + 1);
if (cities == NULL)
{
printf("cannot allocate memory");
return 1;
}
int counter = 0;
for (int y = 0; strcmp(lines[y], "\n") != 0; y++)
{
// don't need this check
if (isdigit(lines[y][0]))
{
char * tok;
struct city *new_city = malloc(sizeof(struct city));
if (new_city == NULL)
{
printf("cannot allocate memory");
free(cities);
return 1;
}
//read first number, not used
tok = strtok(lines[y], " ");
//read coordinate x
tok = strtok(NULL, " ");
printf("tok1: %s\n", tok);
new_city -> x = atof(tok);
//read coordinate y
tok = strtok(NULL, " ");
printf("tok2: %s\n", tok);
new_city -> y = atof(tok);
printf("inserted: %lf\n", new_city -> y);
cities[counter] = *new_city;
counter++;
}
}
fclose(f);
Simply I open the file, read char by char all lines, and then I use strtok() to take the coordinates written (second and third numbers of each line). The problem is that I have to store them into x and y of my city struct, and as I read here atof() must be used, but it approximate the number and then it return segmentation fault, as I printed here (inserted is city->y that is approximated but it is wrong, while tok1 and tok2 are the two correct strings read form the file):
tok1: 334.5909245845
tok2: 161.7809319139
inserted: 161.780932
tok1: 397.6446634067
tok2: 262.8165330708
inserted: 262.816533
tok1: 503.8741827107
tok2: 172.8741151168
inserted: 172.874115
tok1: 444.0479403502
tok2: 384.6491809647
zsh: segmentation fault ./Travelling_salesman_problem ch130.tsp
As you can see comparing the inserted value and tok2, inserted is approximated and then the code breaks. There is a way without changing the code but only the atof() function to have the precise value (because the rest of the code works)?
So many problems.
It is unclear why all the lines are read into char lines[][]; first and then parsed. It makes more sense to read 1 line, parse it into the data structure and then read the next line, etc. Then only one line buffer needed.
Yet assuming there is need to read the entire file first, some code to fix input:
// Avoid naked magic numbers
// char lines[2000][150];
#define LINES_N 2000
#define LINE_SIZE 150
char lines[LINES_N][LINE_SIZE];
int i = 0; // Maybe line_count instead?
int j = 0; // Maybe ch_index instead?
int c = 0; // Good use of int here.
// Append a '\0' at the end to form a _string_.
// Use 1 while loop
// Do not iterate to far, add limits tests
// read file's lines
while (i < LINES_N && (c = getc(f)) != EOF) {
if (j < LINE_SIZE) {
lines[i][j] = c;
j++;
} else {
fprintf(stderr, "Line %d too long\n", i);
}
if (c == '\n') {
lines[i][j] = '\0';
i++;
j = 0;
}
}
// Might as well close the file now, not later.
fclose(f); // and remove later one.
// lines[i - 2] is very bad if i < 2
//strcpy(lines[i - 2], "\n");
// I think all you want here is
// strcpy(lines[i], "\n");
// Since you have `i`, later iterate to `i`,
// no need for strcpy(lines[i], "\n")
//
// for (int y = 0; strcmp(lines[y], "\n") != 0; y++)
for (int y = 0; y < i; y++)
If helpful, later I'll look over the rest.
So I'm trying to do a program that reads a sequence of numbers separated by spaces and new lines. The output should be the same sequence, but erasing unnecessary zeros(The sequence of charachters 'EOF' ends the program). Per example
01492 102934 should come out as 1492 102934
9312 0 01923 should come out as 9312 0 1923
0001249 0000 should come out as 1249 0
Well I've achieved that purpose but have come across a roadblock. The program doesn't exit unless I type the EOF sequence. Maybe it's because I have a while(1) running that gives an infinite loop. But when I try to delete it the program doesn't even print at all. I'm still learning this is for a school project.
Any help would be apreciated!
Here's the code:
#include <stdio.h>
int main(){
char c;
int i=0;
while(1){
c=getchar();
if (i==0){
if(c=='0'){
while (c=='0'){
c=getchar();
}
}
printf("%c",c);
i=i+1;
}
else if (c==' '){
printf("%c",c);
c=getchar();
if(c=='0'){
while (c=='0'){
c=getchar();
}
}
printf("%c",c);
}
else if (c=='E'){
c=getchar();
if (c=='O'){
c=getchar();
if(c=='F'){
printf("\n");
return 0;
}
}
}
else{
printf("%c",c);
}
}
}
The important stuff:
int c; // IMPORTANT, cannot be char
while (1) {
c = getchar();
if (c == EOF) break; // exit loop
// ...
}
There has to be some way to tell the program to exit.
With this, the program will exit on the letter x or two consecutive newlines or entering END.
getchar will return EOF when there is nothing left to read from a file. That can be simulated from stdin ( the keyboard) with ctrl + z on Windows or ctrl + d on Linux.
#include <stdio.h>
#include <string.h>
int main ( void) {
char done[4] = "";
int c = 0;
int prior = 0;
int reading = 0;
int zero = 1;
while ( EOF != ( c = getchar ( )) && 'x' != c) {
if ( '\n' == c && '\n' == prior) {
break;
}
if ( c >= '0' && c <= '9') {
reading = 1;
if ( '0' != c) {
zero = 0;
}
if ( ! zero) {
putchar ( c);
}
}
else {
if ( reading) {
if ( zero) {
putchar ( '0');
}
if ( ' ' == c || '\n' == c) {
putchar ( c);
}
else {
putchar ( ' ');
}
}
reading = 0;
zero = 1;
}
prior = c;
done[0] = done[1];
done[1] = done[2];
done[2] = c;
done[3] = 0;
if ( 0 == strcmp ( done, "END")) {
break;
}
}
putchar ( '\n');
return 0;
}
getchar() returns an int, not a char. If it only returned a char, there would be no way for it to return a value that indicates end of file, since all char values are valid and can’t be used for another purpose.
A motivating example in decimal system may be: A function checks the temperature returns a two-digit number. Any temperature between 0 and 99 is valid. How do you report errors when the thermometer is disconnected? You have to return a number with more digits, and use a special value like UNPLUGGED = 100.
But int is a wider type: it has many more values than char, and the “extra” values can be used to indicate some special condition that means “hey, this is not a valid character, but something else I had to tell you”.
getchar() returns the EOF constant upon failure (any failure), for example if no more input is available. There’s nothing sensible you can do even if the reason for the failure other than end of input. You should end processing at the first EOF.
Thus, change the type of c to int, and every time you call getchar(), you must check that its value is not EOF, and return when you encounter it.
The nested structure of your loops means that EOF checking has to be repeated all over the place. There are other ways to structure the code to keep this check in one place, but, admittedly, the nested loops have at least the potential to exploit the branch predictor, whereas a single getchar followed by a state-machine style switch statement will make it perform potentially worse. None of this matters in a simple homework problem, but it’s something to keep in mind. In any case, performance has to be benchmarked - no other way around it.
Try this code, I think it does what you requested:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static int getLine(char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf("%s", prmpt);
fflush(stdout);
}
if (fgets(buff, sz, stdin) == NULL)
return -2;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff) - 1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? -1 : 0;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff) - 1] = '\0';
return 0;
}
int* convert2numbers(char* arr, int size) {
int i;
int j;
int k;
char token[100];
int* numbers;
int last_space = 0;
int index = 1;
int amount = 1;
// Count the amount of tokens.
for (i = 0; i < size; ++i) {
if (arr[i] == ' ') {
++amount;
}
}
numbers = (int *)malloc(amount * sizeof(int));
numbers[0] = amount;
for (j = 0; j <= size; ++j) {
if (arr[j] == ' ' || arr[j] == '\0') {
// Copy token from input string.
for (k = 0; k < j; ++k) {
token[k] = arr[k + last_space];
}
token[j] = '\0';
numbers[index] = atoi(token);
// Clear the token and continue.
memset(token, '\0', sizeof(token));
last_space = j;
++index;
}
}
return numbers;
}
int main(void) {
int i;
int size;
int* numbers;
int amount;
char input[100];
char help[] = "Numbers> ";
printf("Input numbers below or press enter to exit!\n");
while (1) {
getLine(help, input, sizeof(input));
// If input is empty exit.
if (input[0] == '\0') {
break;
}
size = strlen(input);
numbers = convert2numbers(input, size);
amount = numbers[0];
for (i = 1; i < amount + 1; ++i) {
printf("%d ", numbers[i]);
}
printf("\n");
}
return 0;
}
When run with these inputs this code outputs:
Input numbers below or press enter to exit!
Numbers> 01492 102934
1492 102934
Numbers> 9312 0 01923
9312 0 1923
Numbers> 0001249 0000
1249 0
Also if you press enter in console, it exits, as to escape the while(1) loop, easily.
I would like to read 3-digit-numbers with spaces inbetween from a file with the fgetc()-command and put them into an array, which is not currently working, as the resulting array has completely different objects in it. What am I doing wrong? (I used a file with "107 313 052 614" in it, resulting in the output "5435 5641 5380 5942")
My Code:
#include<stdio.h>
#include<stdlib.h>
void print_array(int* arrayp, int lengthp){
int i;
for(i=0;i<lengthp;i++){
printf("%i ", arrayp[i]);
}
return;
}
int main(){
int length=1;
int i;
FILE *fp1;
fp1 = fopen("durations.txt", "r");
fgetc(fp1);fgetc(fp1);fgetc(fp1);
while(fgetc(fp1)!=EOF){
length++;
fgetc(fp1);
fgetc(fp1);
fgetc(fp1);
}
fclose(fp1);
int* list = (int*) malloc(sizeof(int)*length);
FILE *fp2;
fp2 = fopen("durations.txt", "r");
for(i=0;i<length;i++){
list[i]=0;
list[i]+=100*(fgetc(fp2));
list[i]+=10*(fgetc(fp2));
list[i]+=(fgetc(fp2));
fgetc(fp2);
}
fclose(fp2);
print_array(list, length);
return 0;
}
The characters that are used to store digits in a "readable" file are not "numbers". The most popular encoding is ascii character encoding, ex. the 1 digit is represented with the number 49 in decimal.
Because the 0, 1, 2 ... 9 digits in ascii encoding are encoded in increasing order, you can just substract 48 (ie. '0' character) to convert a digit character to it's machine format Just - '0'.
Change you loop into:
for(i=0;i<length;i++){
list[i]=0;
list[i]+=100*(fgetc(fp2) - '0');
list[i]+=10*(fgetc(fp2) - '0');
list[i]+=(fgetc(fp2) - '0');
fgetc(fp2);
}
This also explains the current output of your program. If you don't substract '0' from the numbers, then for example for 107 you get:
100 * '1' + 10 * '0' + '7' =
100 * 49 + 10 * 48 + 55 =
5435
The 49, 48 and 55 are decimal values for digits 1, 0 and 7 in ascii table.
The problem is that your are reading in the (probably) ASCII values of each digit and assuming that is the value of the digit. You need to subtract the value of the zero character from each value, like this:
for (i = 0; i < length; i++) {
list[i] = 0;
list[i] += 100 * (fgetc(fp2)-'0');
list[i] += 10 * (fgetc(fp2)-'0');
list[i] += (fgetc(fp2)-'0');
fgetc(fp2);
}
This will work even if your system doesn't use ASCII encoding.
It might be simpler to just read the numbers into cstrings then use the stdlib function atoi to convert each string to a number before loading into the array of ints:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
//open the file for reading with error checking:
FILE* fp;
char c = '0';
int length = 0;
fp = fopen("durations.txt", "r");
if (fp == NULL) {
printf("Could not open file\n");
return 0;
}
//count the number of lines in a EOF-terminated string w/o newlines
for (c = fgetc(fp); c != EOF; c = fgetc(fp)) {
if (c == ' ') {
length += 1;
}
}
rewind(fp); //go back to file end w/o needing close/open
//Then, assuming only spaces are between numbers (so length = length + 1)
char buffer[4];
int* lst = (int*) malloc(sizeof(int)*length);
for (int i = 0; i < length + 1; i++) {
fscanf(fp, "%s", buffer); //stops at spaces
lst[i] = atoi(buffer);
}
//close the file with error checking
int check = fclose(fp);
if (check != 0) {
printf("File close failed");
return 0;
}
for (int i = 0; i < length + 1; i++) { //print result
printf("%d\n", lst[i]);
}
free(lst); //clean up memory after use
lst = NULL;
return 0;
}
I've been having some problems with this code below...
The main idea of the code is to read line by line and convert chars strings into floats and save the floats in a array called nfloat.
The input is a .txt containing this: n = the number of strings, in this case n = 3
3
[9.3,1.2,87.9]
[1.0,1.0]
[0.0,0.0,1.0]
The first number, 3 is the number of vectors as we can see in the image, but that number isn't static, the input can be 5 or 7, etc instead of 3.
So far, I've started doing the following, (for only 1 vector case) but the code has some memory errors I think:
int main(){
int n; //number of string, comes in the input
scanf("%d\n", &n);
char *line = NULL;
size_t len = 0;
ssize_t read;
read = getline(&line,&len,stdin); //here the program assigns memory for the 1st string
int numsvector = NumsVector(line, read);//calculate the amount of numbers in the strng
float nfloat[numsvector];
int i;
for (i = 0; i < numsvector; ++i)
{
if(numsvector == 1){
sscanf(line, "[%f]", &nfloat[i]);
}
else if(numsvector == 2){
if(i == 0) {
sscanf(line, "[%f,", &nfloat[i]);
printf("%f ", nfloat[i]);
}
else if(i == (numsvector-1)){
sscanf((line+1), "%f]", &nfloat[i]);
printf("%f\n", nfloat[i]);
}
}
else { //Here is where I think the problems are
if(i == 0) {
sscanf(line, "[%f,", &nfloat[i]);
printf("%f\n", nfloat[i]);
}
else if(i == (numsvector-1)) {
sscanf((line+1+(4*i)), "%f]", &nfloat[i]);
printf("%f\n", nfloat[i]);
}
else {
sscanf((line+1+(4*i)), "%f,", &nfloat[i]);
printf("%f\n", nfloat[i]);
}
}
}
Well, the problems come with the sscanf instructions I think, in the case of a string with two floats or one, the code works fine but in the case of 3 or more floats, the code doesn't work well and I can't understand why...
Here I attach the function too, but It seems to be correct... the focus of the problem remains on the main.
int NumsVector(char *linea, ssize_t size){
int numsvector = 1; //minimum value = 1
int n;
for(n = 2; n<= size; n++){
if (linea[n] != '[' && linea[n] != ']'){
if(linea[n] == 44){
numsvector = numsvector + 1;
}
}
}
return numsvector;
}
Please could someone help me understand where is the problem?
Ok - if you replace your current for loop with this, your nfloat array should end up with the right numbers in it.
/* Replaces the end ] with a , */
line[strlen(line) - 1] = ',';
/* creates a new pointer, pointing after the first [ in the original string */
char *p = line + 1;
do
{
/* grabs up to the next comma as a float */
sscanf(p, "%f,", &nfloat[i]);
/* prints the float it's just grabbed to 2 dp */
printf("%.2f\n",nfloat[i]);
/* moves pointer forward to next comma */
while (*(p++) != ',');
}
while (++i < numsvector); /* stops when you've got the expected number */
I have written a small program which takes input of a file such as:
13,22,13,14,31,22, 3, 1,12,10
11, 4,23, 7, 5, 1, 9,33,11,10
40,19,17,23, 2,43,35,21, 4,34
30,25,16,12,11, 9,87,45, 3, 1
1,2,3,4,5,6,7,8,9,10
and outputs the largest sum of numbers on each line that is less than 50.
However if the inputted file has a trailing newline character the loop runs one too many times and hence another line is added to the array with random data. So I'm looking for a better way to do this comparison to avoid this issue. I'm also assuming all lines have 10 integers on at the moment as i cannot think of a better way to do the end of line loop comparison.
#include <stdio.h>
#include <stdlib.h>
void readLineData(int lineNo, int val[][10], FILE *fp);
int findSum(int lineNo, int val[][10], FILE *fp);
int main(int argc, char *argv[]) {
FILE *fp;
int val[5][10];
// Open file.
if ((fp = fopen(argv[1], "r")) == NULL)
{
perror("Cannot open file ");
exit(EXIT_FAILURE);
}
for (int i = 0; !feof(fp); i++) // runs too many times if file ends with '\n'
{
readLineData(i, val, fp);
printf("%d\n", findSum(i, val, fp));
}
fclose(fp);
return EXIT_SUCCESS;
}
void readLineData(int lineNo, int val[][10], FILE *fp) {
char c;
for (int i = 0; i < 10; i++) // assuming line contains 10 integers
{
fscanf(fp, "%d,", &val[lineNo][i]);
}
}
int findSum(int lineNo, int val[][10], FILE *fp) {
int highVal = 0;
int value1 = 0;
int value2 = 0;
for(int i = 0; i < 10; i++) //each letter
{
for(int j = 0; j < 10; j++)// every other letter
{
if((val[lineNo][i] + val[lineNo][j]) > highVal && i != j && (val[lineNo][i] + val[lineNo][j]) <= 50)
{
highVal = val[lineNo][i] + val[lineNo][j];
value1 = val[lineNo][i];
value2 = val[lineNo][j];
}
}
}
printf("Line %d: largest pair is %d and %d, with a total of: ", lineNo+1, value1, value2);
return highVal;
}
any help with those loop comparisons and general notation tips is most welcome.
Thanks
The posted code does not distinguish between two lines that have five integers and (the expected) one line that has 10 integers. Suggest reading in a line at a time, using fgets() and then using sscanf() on the read line to ensure that all the read integers belong to the same line.
Check the return value of input operations. For example, sscanf() (and fscanf()) return the number of assignments made. Only process lines that have the expected 10 integers, which would detect invalid lines including the trailing empty line.
For example:
/* Returns 1 on success and 0 on failure. */
int readLineData(int lineNo, int val[][10], FILE *fp)
{
char line[1024]; /* Arbitrarily large. */
if (fgets(line, sizeof(line), fp))
{
/* %n records position where processing ended. */
int pos;
const int result = sscanf(line,
"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n",
&val[lineNo][0],
&val[lineNo][1],
&val[lineNo][2],
&val[lineNo][3],
&val[lineNo][4],
&val[lineNo][5],
&val[lineNo][6],
&val[lineNo][7],
&val[lineNo][8],
&val[lineNo][9],
&pos);
/* 10 integers and full line processed,
except if new-line character present. */
return 10 == result &&
(pos == strlen(line) ||
(pos + 1 == strlen(line) && '\n' == line[pos]));
}
return 0;
}
You could simply consume the newline character yourself:
for (int i = 0; !feof(fp); i++) // runs too many times if file ends with '\n'
{
readLineData(i, val, fp);
printf("%d\n", findSum(i, val, fp));
fscanf(fp, "%*c"); // read a character without storing it in a variable
}
Note that there are undoubtedly better ways that involve reading an entire line at once and simply examining its contents; but this is the easiest way that will fit with what you already have.
you could check if fscanf fails in your readLineData function:
int readLineData(int lineNo, int val[][10], FILE *fp) {
for (int i = 0; i < 10; i++) {// assuming line contains 10 integers
if (fscanf(fp, "%d,", &val[lineNo][i]) != 1) {
return 1;
}
}
return 0;
}