#include <stdio.h>
#include <stdlib.h>
void *salloc(int x){
char **pointer;
int i;
pointer = malloc(sizeof(char)*x);
if(pointer == NULL){
exit(-1);
}
for(i=0; i<x; i++){
pointer[i] = malloc(sizeof(char) * 20);
if(pointer[i] == NULL){
exit(-1);
}
}
return pointer;
}
void Input(int value, char **array){
for(i = 0; i < value; i++){
printf("%d ----\n", i);
fgets(array[i], 20, stdin);
printf("%d ----\n", i);
}
}
int main(int argc, char *argv[]){
char **array;
int value = 2;
array = salloc(value);
Input(value, array);
return 0;
}
The general idea, can be that I miss some syntax.
So I want to read in a string with spaces. If I run this for the value 2, it will print:
0 ----
0 ----
1 ----
"some string"
and it crashes after I press enter.
If I do this with value 1:
it immediately crashes.
However if I replace fgets() with:
scanf("%s", array[i]);
it works (except for the spaces).
So how does fgets() work in 2d-arrays?
Because I get it to work in 1d-arrays. And for some reason I can print 1d-arrays from row 2 when the array only has 2 rows, so it should only be able to print from rows 0 and 1 right?
Here is a demonstrative program that shows how fgets can be used with a 2D array.
#include <stdio.h>
#define N 5
#define M 10
int main( void )
{
char lines[N][M];
size_t n = 0;
while( n < N && fgets( lines[n], sizeof( *lines ), stdin ) != NULL ) ++n;
for ( size_t i = 0; i < n; i++ ) puts( lines[i] );
return 0;
}
If to enter for example
One
Two
Three
Four
Five
then the program output will be the same
One
Two
Three
Four
Five
When you do
pointer = malloc(sizeof(char)*x);
you only allocate x characters (i.e. bytes), not pointers to characters. Change to
pointer = malloc(sizeof(char*)*x);
Without the change, you might go out of bounds and have undefined behavior. And this is exactly what happens in your code, you allocate only two bytes to store two pointers, and a single pointer is either four or eight bytes, so you don't allocate enough memory for even a single pointer.
Undefined behavior is a common cause of crashes, but sometimes it might also seem to work.
Note also when taking input with fgets (or any of the line-oriented input functions), fgets will read up to, and include, the '\n' at the end of each line read. You should perform 2 additional tests/operations. (1) you should test that the last character read by fgets is in fact the '\n' character. If it is not, that will indicate your input was truncated by fgets at the length you specified in the second parameter to fgets, and additional character remain unread for that line. Without this check and some way to handle lines that exceed the specified width, you next call to fgets will read the remaining characters for the current line as your next line of input.
(2) you should remove the newline included by fgets to prevent your strings from containing embedded '\n' characters at the end. (if you are simply parsing numbers from the line and not storing it as a string, this can be handled in several different manners). But, for the general case, you use strlen to locate the end of the string, and then overwrite the '\n' with a nul-terminating character.
In addition to the above, it may make more sense to allocate memory for each individual pointer in array only after a line of data has been read to prevent over-allocating space in your code. Since you are specifying that the allocation size for each string will be 20, a simple character buffer of that size can be used to take input, and after confirming input, you can allocate storage for that line in array. A short example of your function with the checks included and with allocation included in input would be:
#define MAXC 20 /* max chars per read */
...
void Input (int value, char **array)
{
int i = 0;
size_t len = 0;
char buf[MAXC] = {0};
while (i < value && fgets (buf, MAXC, stdin)) {
printf ("%d ----\n", i);
len = strlen (buf); /* get length */
if (len + 1 == MAXC && buf[len-1] != '\n') /* validate read */
fprintf (stderr, "warning: chars exceed MAXC, line[%d]\n", i);
else
buf[--len] = 0; /* strip '\n' */
printf ("%d ----\n", i);
array[i++] = strdup (buf); /* allocate/copy */
}
}
Lastly, why choose void as the function type? Why not return the number of values read into array if values are successfully read into array, or 0 otherwise. This will at least allow some indication of success or failure of your read and provide a way of returning the number of lines allocated back to the calling function.
An example of your code incorporating the adjusted allocation, necessary checks, and useful return types would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 20
void *salloc (int x)
{
char **pointer;
pointer = malloc (sizeof *pointer * x);
if (pointer == NULL)
exit(-1);
return pointer;
}
int input (int value, char **array)
{
int i = 0;
size_t len = 0;
char buf[MAXC] = {0};
while (i < value && fgets (buf, MAXC, stdin)) {
printf ("%d ----\n", i);
len = strlen (buf); /* get length */
if (len + 1 == MAXC && buf[len-1] != '\n') /* validate read */
fprintf (stderr, "warning: chars exceed MAXC, line[%d]\n", i);
else
buf[--len] = 0; /* strip '\n' */
printf ("%d ----\n", i);
array[i++] = strdup (buf); /* allocate/copy */
}
return i;
}
int main (int argc, char *argv[]) {
int i, nlines, value = argc > 1 ? atoi (argv[1]) : 2;
char **array;
array = salloc (value);
if (!(nlines = input (value, array))) /* validate input */
return 1;
for (i = 0; i < nlines; i++) /* print input */
printf (" array[%2d] : %s\n", i, array[i]);
for (i = 0; i < nlines; i++) /* free memory */
free (array[i]);
free (array);
return 0;
}
Test Input File
The following is a test input file where line 1 (the 2nd line) exceedS 20 characters:
$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Example Output
$ ./bin/readarray 10 <../dat/captnjack.txt
0 ----
0 ----
1 ----
warning: chars exceed MAXC, line[1]
1 ----
2 ----
2 ----
3 ----
3 ----
array[ 0] : This is a tale
array[ 1] : Of Captain Jack Spa
array[ 2] : rrow
array[ 3] : A Pirate So Brave
array[ 4] : On the Seven Seas.
Don't forget to use a memory error check program like valgrind to validate your use of the memory you allocate and to insure you have freed it when it is no longer needed. Let me know if you have any additional questions.
Related
my code would work in this way:
input : a[]="create /dir/bar"
and save in this string:
b[]=create
c[]=/dir/bar
there is also a case in which i save an other string: (for example)
a[]=write /foo/bar "test"
b[]= write
c[]=/foo/bar
d[]=test (without the "")
my code is this :
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SPACE ' '
void divide(char a[], char b[], char c[], char d[]);
int main(int argc, char const *argv[]) {
char a[50+1];
char b[50+1];
char c[50+1];
char d[50+1];
int i;
scanf("%s\n", a);
divide(a, b, c, d);
for(i=0; b[i]!='\0'; i++)
printf("%s %s %s \n", b, c, d);
return 0;
}
void divide(char a[], char b[], char c[], char d[]){
int i, j;
for(i=0; a[i]!=SPACE; i++)
b[i]=a[i];
b[i]='\0';
for(; a[i]==SPACE; i++)
;
for(j=0; a[i]!='\0'; i++, j++)
c[j]=a[i];
c[j]='\0';
for(; a[i]==SPACE; i++)
;
if(a[i]=='"'){
i++;
for(j=0; a[i]!='"'; i++)
d[j]=a[i];
d[j]='\0';
return;
}
}
but it does not work for a segmentation fault after the program get the input. where is the problem?
I must not use malloc, because it spend too much time to work (I have to get thousands of these lines) and does not respect a limit. (I work for a project in my university)
You may be making this a little more difficult than it needs to be. Yes, you can tokenize a string by repeated calls to sscanf or with repeated reads with scanf, but the C library provides a tool to tokenize words from a line of text. Smartly enough named strtok.
You simply declare a constant string holding the delimiters you wish to break the words on (e.g. delims = " \t"; to break the words on space or tab, and then call strtok (str, delims) to return the first token (word), and then loop over repeated calls to strtok (NULL, delims) to parse the remaining words (or until you reach your max of 3 words).
(note the first call to strtok uses str as the first parameter, while all subsequent calls use NULL)
This is a far more flexible way to handle an unknown number of tokens in a string.
Instead of using a[], b[], c[], etc.. consider using just a single buf[] to read the line of input into, and then an array of strings to hold the parameters (which allows you to use an index variable during your loops over strtok to assign and copy the correct string to the associated index).
Don't use void as a return in circumstances like this. Why not use a meaningful return (like the number of parameters in the line of text). That way, you know how many were read (or tokenized) in your divide function. Give it a return that can provide useful information, e.g.
size_t divide (char *buf, char (*params)[MAXC+1]);
Which will now return a size_t type containing the number of parameters that result from each call to divide.
Putting it altogether, (and using fgets to read the entire line of input), you could do something like the following:
#include <stdio.h>
#include <string.h>
enum { MAXP = 3, MAXC = 50 }; /* max parameters & chars */
size_t divide (char *buf, char (*params)[MAXC+1]);
int main (void) {
char buf[MAXC * 4 + 1] = "";
char params[MAXP][MAXC + 1]; /* array to hold 3 parameters */
size_t i, len, nparams = 0;
/* use fgets for line-oriented user input */
printf ("\nenter commands: ");
if (!fgets (buf, sizeof buf, stdin)) {
fprintf (stderr, "error: insufficient input.\n");
return 1;
}
len = strlen (buf); /* get length */
if (buf[len - 1] == '\n') /* validate last char is '\n' */
buf[--len] = 0; /* overwrite with nul-terminating char */
else { /* short read -- handle error */
fprintf (stderr, "error: incomplete input read.\n");
return 1;
}
nparams = divide (buf, params);
for (i = 0; i < nparams; i++)
printf ("parameter[%zu] : %s\n", i, params[i]);
return 0;
}
/* divide using strtok */
size_t divide (char *buf, char (*params)[MAXC+1])
{
char *delims = " \t", /* delimiters for strtok */
*p = buf; /* pointer to buf */
size_t n = 0; /* var to return number of params */
p = strtok (buf, delims); /* tokenize fist paramter */
while (p) { /* now loop until all words exhausted or limit reached */
strncpy (params[n++], p, MAXC); /* copy token to params array */
if (n == MAXP) /* check if limit reached */
break;
p = strtok (NULL, delims); /* get next token */
}
return n; /* return the number of parameters found */
}
Example Use/Output
$ /bin/splitparams
enter commands: create /dir/bar
parameter[0] : create
parameter[1] : /dir/bar
$ ./bin/splitparams
enter commands: write /foo/bar "test"
parameter[0] : write
parameter[1] : /foo/bar
parameter[2] : "test"
Or providing a bunch of extra words (to validate handling of only 3)
$ ./bin/splitparams
enter commands: write /foo/bar "test" and some more stuff
parameter[0] : write
parameter[1] : /foo/bar
parameter[2] : "test"
If you run this simple program
#include<stdio.h>
int main(int argc, char const *argv[]) {
char a[50+1];
scanf("%s\n", a);
printf("|%s|\n", a);
return 0;
}
and give the input "create foo", you'll get the output
|create|
As you can see you only got the first word, i.e. "create", instead of the expected "create foo" as
scanf("%s\n", a);
will only give the first word. Consequently your divide function will fail. Instead of scanf you could do
fgets(a, 51, stdin);
to make sure the whole input is read into array a.
In general your program lacks a lot of range checking and input validation. You should add that.
Another problem I see is that in case the input is
create /dir/bar
you never initialize the string d but you still print it in main. That is undefined behaviour.
Try:
char d[50+1];
d[0] = '\0'; // Add this line
I tried to get the inputs(strings) from user and store them in an array.But after I ran this code, the program instantly crashed.
#include <stdio.h>
int main() {
int i;
char *word[3];
for(i=0;i<3;i++)
{
printf(" Enter a word: ");
scanf("%s", &word[i]);
}
printf("%s ", word[0]);
return 0;
}
In this line:
scanf("%s", &word[i]);
You need to make sure word[i] is pointing somewhere, and has enough space to occupy the string entered. Since word[i] is a char * pointer, you need to at some time allocate memory for this. Otherwise, it is just a dangling pointer not pointing anywhere.
If you want to stick with scanf(), then you can allocate some space beforehand with malloc.
malloc() allocates requested memory on the heap, then returns a void* pointer at the end.
You can apply malloc() in your code like this:
size_t malloc_size = 100;
for (i = 0; i < 3; i++) {
word[i] = malloc(malloc_size * sizeof(char)); /* allocates 100 bytes */
printf("Enter word: ");
scanf("%99s", word[i]); /* Use %99s to avoid overflow */
/* No need to include & address, since word[i] is already a char* pointer */
}
Note: Must check return value of malloc(), because it can return NULL when unsuccessful.
Additionally, whenever you allocate memory with the use of malloc(), you must use free to deallocate requested memory at the end:
free(word[i]);
word[i] = NULL; /* safe to make sure pointer is no longer pointing anywhere */
Another approach without scanf
A more proper way to read strings should be with fgets.
char *fgets(char *str, int n, FILE *stream) reads a line from an input stream, and copies the bytes over to char *str, which must be given a size of n bytes as a threshold of space it can occupy.
Things to note about fgets:
Appends \n character at the end of buffer. Can be removed easily.
On error, returns NULL. If no characters are read, still returns NULL at the end.
Buffer must be statically declared with a given size n.
Reads specified stream. Either from stdin or FILE *.
Here is an example of how it can be used to read a line of input from stdin:
char buffer[100]; /* statically declared buffer */
printf("Enter a string: ");
fgets(buffer, 100, stdin); /* read line of input into buffer. Needs error checking */
Example code with comments:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUMSTR 3
#define BUFFSIZE 100
int main(void) {
char *words[NUMSTR];
char buffer[BUFFSIZE];
size_t i, count = 0, slen; /* can replace size_t with int if you prefer */
/* loops only for three input strings */
for (i = 0; i < NUMSTR; i++) {
/* read input of one string, with error checking */
printf("Enter a word: ");
if (fgets(buffer, BUFFSIZE, stdin) == NULL) {
fprintf(stderr, "Error reading string into buffer.\n");
exit(EXIT_FAILURE);
}
/* removing newline from buffer, along with checking for overflow from buffer */
slen = strlen(buffer);
if (slen > 0) {
if (buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
printf("Exceeded buffer length of %d.\n", BUFFSIZE);
exit(EXIT_FAILURE);
}
}
/* checking if nothing was entered */
if (!*buffer) {
printf("No string entered.\n");
exit(EXIT_FAILURE);
}
/* allocate space for `words[i]` and null terminator */
words[count] = malloc(strlen(buffer)+1);
/* checking return of malloc, very good to do this */
if (!words[count]) {
printf("Cannot allocate memory for string.\n");
exit(EXIT_FAILURE);
}
/* if everything is fine, copy over into your array of pointers */
strcpy(words[count], buffer);
/* increment count, ready for next space in array */
count++;
}
/* reading input is finished, now time to print and free the strings */
printf("\nYour strings:\n");
for (i = 0; i < count; i++) {
printf("words[%zu] = %s\n", i, words[i]);
free(words[i]);
words[i] = NULL;
}
return 0;
}
Example input:
Enter a word: Hello
Enter a word: World
Enter a word: Woohoo
Output:
Your strings:
words[0] = Hello
words[1] = World
words[2] = Woohoo
There seems to be a bit of confusion in this area. Your primary problem is you are attempting to write each word to the address of each of pointers you declare with char *word[3];. (not to mention you have no storage allocated at the location pointed to by each pointer -- but you never get there as you attempt to write to the address of each pointer with &word[i] rather than to the pointer itself)
While you can use scanf you will quickly run into one of the many pitfalls with taking user input with scanf that plague all new C programmers (e.g. failing to handle the '\n' left in the input buffer, failing to handle whitespace in strings, failing to limit the number of characters read/written, failing to validate the read or handle EOF, etc...)
A better approach is to simply use fgets and then trim the '\n' that fgets read and includes in the buffer to which it stores the string. A simple example would be:
#include <stdio.h>
#include <string.h>
#define NWDS 3 /* declare a constant for the maximum number of words */
int main (void) {
int i, n = 0;
char word[NWDS][50] = { "" }; /* provide storage or allocate */
for (i = 0; i < NWDS; i++) { /* for a max of NWDS */
printf ("Enter word : "); /* prompt */
if (!fgets (word[i], sizeof word[i], stdin)) /* read/validate */
break; /* protect against EOF */
size_t len = strlen (word[i]); /* get length */
if (word[i][len-1] == '\n') /* check for trailing '\n' */
word[i][--len] = 0; /* overwrite with nulbyte */
}
n = i; /* store number of words read */
putchar ('\n'); /* make it pretty */
for (i = 0; i < n; i++) /* output each word read */
printf (" word[%d] : %s\n", i, word[i]);
#if (defined _WIN32 || defined _WIN64)
getchar(); /* keep terminal open until keypress if on windows */
#endif
return 0;
}
Go ahead and cancel input at any time by generating an EOF during input (ctrl + d on Linux or ctrl + z on windoze), you are covered.
Example Use/Output
$ ./bin/wordsread
Enter word : first word
Enter word : next word
Enter word : last word
word[0] : first word
word[1] : next word
word[2] : last word
Looks things over, consider the other answers, and let me know if you have further questions.
char *word[3]; // <-- this is an array of 3 dangling pointers, of type char*
// they still point nowhere, we later need to set them to some allocated location.
...
for(i=0;i<3;i++) {
word[i] = malloc(some_max_size * sizeof(char)); // <-- allocate space for your word
printf(" Enter a word: ");
scanf("%s", word[i]); // <-- not &word[i]; word[i] is already a char* pointer
}
You are declaring word as array of pointer (char *word[3];). You have to allocate memory to store data. Allocate memory with malloc or similar functions before assigning values.
Yes the code crashes because declaring an array of character
pointers is not enough, you need to set the pointers to point
to memory where the strings can be stored.
E.g.
const int maxLen = 32;
char* word[3] = {NULL,NULL,NULL};
word[i] = malloc(maxLen);
then read the string from keyboard, to ensure that the string is not too
long use fgets and maxLen:
printf("Enter a word:");
fgets(word[i],maxLen,stdin);
#include <stdio.h>
int main(){
int n;
int i=0;
scanf("%d",&n);
char arr[n];
while(n>i){
scanf("%s",&arr[i]);
i+=1;
}
while(n-i<n){
printf(" %c ",arr[n-i]);
i-=1;
}
}
The code char *word[3] made a 3-element array of pointers!
See, you have basically created a character array of pointers, so you cannot put a "string" into each one of them, because the type of a pointer variable is long hexadecimal.
I am trying to save each line of a text file into an array.
They way I am doing it and works fine so far is this :
char *lines[40];
char line[50];
int i = 0 ;
char* eof ;
while( (eof = fgets(line, 50, in)) != NULL )
{
lines[i] = strdup(eof); /*Fills the array with line of the txt file one by one*/
i++;
}
My text file has 40 lines , which I am accessing with a for loop
for( j = 0; j <= 39 ; j++)
{ /*Do something to each line*/}.
So far so good. My problem is that i define the size of the array lines
for the a text file that has 40 lines. I tried to count the lines and then define the size but I am getting segmentation fault.
My approach:
int count=1 ; char c ;
for (c = getc(in); c != EOF; c = getc(in))
if (c == '\n') // Increment count if this character is newline
count = count + 1;
printf("\nNUMBER OF LINES = %d \n",count);
char* lines[count];
Any ideas ?
As an aside, I tested the exact code you show above to get line count (by counting newline characters), on a file containing more than 1000 lines, and with some lines 4000 char long. The problem is not there.
The seg fault is therefore likely due to the way you are allocating memory for each line buffer. You may be attempting to write a long line to a short buffer. (maybe I missed it in your post, but could not find where you addressed line length?)
Two things useful when allocating memory for storing strings in a file are number of lines, and the maximum line length in the file. These can be used to create the array of char arrays.
You can get both line count and longest line by looping on fgets(...): (a variation on your theme, essentially letting fgets find the newlines)
int countLines(FILE *fp, int *longest)
{
int i=0;
int max = 0;
char line[4095]; // max for C99 strings
*longest = max;
while(fgets(line, 4095, fp))
{
max = strlen(line);
if(max > *longest) *longest = max;//record longest
i++;//track line count
}
return i;
}
int main(void)
{
int longest;
char **strArr = {0};
FILE *fp = fopen("C:\\dev\\play\\text.txt", "r");
if(fp)
{
int count = countLines(fp, &longest);
printf("%d", count);
GetKey();
}
// use count and longest to create memory
strArr = create2D(strArr, count, longest);
if(strArr)
{
//use strArr ...
//free strArr
free2D(strArr, lines);
}
......and so on
return 0;
}
char ** create2D(char **a, int lines, int longest)
{
int i;
a = malloc(lines*sizeof(char *));
if(!a) return NULL;
{
for(i=0;i<lines;i++)
{
a[i] = malloc(longest+1);
if(!a[i]) return NULL;
}
}
return a;
}
void free2D(char **a, int lines)
{
int i;
for(i=0;i<lines;i++)
{
if(a[i]) free(a[i]);
}
if(a) free(a);
}
There are many ways to approach this problem. Either declare a static 2D array or char (e.g. char lines[40][50] = {{""}};) or declare a pointer to array of type char [50], which is probably the easiest for dynamic allocation. With that approach you only need a single allocation. With constant MAXL = 40 and MAXC = 50, you simply need:
char (*lines)[MAXC] = NULL;
...
lines = malloc (MAXL * sizeof *lines);
Reading each line with fgets is a simple task of:
while (i < MAXL && fgets (lines[i], MAXC, fp)) {...
When you are done, all you need to do is free (lines); Putting the pieces together, you can do something like:
#include <stdio.h>
#include <stdlib.h>
enum { MAXL = 40, MAXC = 50 };
int main (int argc, char **argv) {
char (*lines)[MAXC] = NULL; /* pointer to array of type char [MAXC] */
int i, n = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* valdiate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
if (!(lines = malloc (MAXL * sizeof *lines))) { /* allocate MAXL arrays */
fprintf (stderr, "error: virtual memory exhausted 'lines'.\n");
return 1;
}
while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */
char *p = lines[n]; /* assign pointer */
for (; *p && *p != '\n'; p++) {} /* find 1st '\n' */
*p = 0, n++; /* nul-termiante */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
/* print lines */
for (i = 0; i < n; i++) printf (" line[%2d] : '%s'\n", i + 1, lines[i]);
free (lines); /* free allocated memory */
return 0;
}
note: you will also want to check to see if the whole line was read by fgets each time. (say you had a long line of more than 38 chars in the file). You do this by checking whether *p is '\n' before overwriting with the nul-terminating character. (e.g. if (*p != '\n') { int c; while ((c = getchar()) != '\n' && c != EOF) {} }). That insures the next read with fgets will begin with the next line, instead of the remaining characters in the current line.
To include the check you could do something similar to the following (note: I changed the read loop counter from i to n to eliminate the need for assigning n = i; following the read loop).
while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */
char *p = lines[n]; /* assign pointer */
for (; *p && *p != '\n'; p++) {} /* find 1st '\n' */
if (*p != '\n') { /* check line read */
int c; /* discard remainder of line with getchar */
while ((c = fgetc (fp)) != '\n' && c != EOF) {}
}
*p = 0, n++; /* nul-termiante */
}
It is up to you whether you discard or keep the remainder of lines that exceed the length of your array. However, it is a good idea to always check. (the lines of text in my example input below are limited to 17-chars so there was no possibility of a long line, but you generally cannot guarantee the line length.
Example Input
$ cat dat/40lines.txt
line of text - 1
line of text - 2
line of text - 3
line of text - 4
line of text - 5
line of text - 6
...
line of text - 38
line of text - 39
line of text - 40
Example Use/Output
$ ./bin/fgets_ptr2array <dat/40lines.txt
line[ 1] : 'line of text - 1'
line[ 2] : 'line of text - 2'
line[ 3] : 'line of text - 3'
line[ 4] : 'line of text - 4'
line[ 5] : 'line of text - 5'
line[ 6] : 'line of text - 6'
...
line[38] : 'line of text - 38'
line[39] : 'line of text - 39'
line[40] : 'line of text - 40'
Now include a the length check in code and add a long line to the input, e.g.:
$ cat dat/40lines+long.txt
line of text - 1
line of text - 2
line of text - 3 + 123456789 123456789 123456789 123456789 65->|
line of text - 4
...
Rerun the program and you can confirm you have now protected against long lines in the file mucking up your sequential read of lines from the file.
Dynamically Reallocating lines
If you have an unknown number of lines in your file and you reach your initial allocation of 40 in lines, then all you need do to keep reading additional lines is realloc storage for lines. For example:
int i, n = 0, maxl = MAXL;
...
while (fgets (lines[n], MAXC, fp)) { /* read each line */
char *p = lines[n]; /* assign pointer */
for (; *p && *p != '\n'; p++) {} /* find 1st '\n' */
*p = 0; /* nul-termiante */
if (++n == maxl) { /* if limit reached, realloc lines */
void *tmp = realloc (lines, 2 * maxl * sizeof *lines);
if (!tmp) { /* validate realloc succeeded */
fprintf (stderr, "error: realloc - virtual memory exhausted.\n");
break; /* on failure, exit with existing data */
}
lines = tmp; /* assign reallocated block to lines */
maxl *= 2; /* update maxl to reflect new size */
}
}
Now it doesn't matter how many lines are in your file, you will simply keep reallocating lines until your entire files is read, or you run out of memory. (note: currently the code reallocates twice the current memory for lines on each reallocation. You are free to add as much or as little as you like. For example, you could allocate maxl + 40 to simply allocate 40 more lines each time.
Edit In Response To Comment Inquiry
If you do want to use a fixed increase in the number of lines rather than scaling by some factor, you must allocate for a fixed number of additional lines (the increase times sizeof *lines), you can't simple add 40 bytes, e.g.
void *tmp = realloc (lines, (maxl + 40) * sizeof *lines);
if (!tmp) { /* validate realloc succeeded */
fprintf (stderr, "error: realloc - virtual memory exhausted.\n");
break; /* on failure, exit with existing data */
}
lines = tmp; /* assign reallocated block to lines */
maxl += 40; /* update maxl to reflect new size */
}
Recall, lines is a pointer-to-array of char[50], so for each additional line you want to allocate, you must allocate storage for 50-char (e.g. sizeof *lines), so the fixed increase by 40 lines will be realloc (lines, (maxl + 40) * sizeof *lines);, then you must accurately update your max-lines-allocated count (maxl) to reflect the increase of 40 lines, e.g. maxl += 40;.
Example Input
$ cat dat/80lines.txt
line of text - 1
line of text - 2
...
line of text - 79
line of text - 80
Example Use/Output
$ ./bin/fgets_ptr2array_realloc <dat/80lines.txt
line[ 1] : 'line of text - 1'
line[ 2] : 'line of text - 2'
...
line[79] : 'line of text - 79'
line[80] : 'line of text - 80'
Look it over and let me know if you have any questions.
I am working on a project for school and I have run into a little bit of trouble. The gist of the project is to write a program that reads in a file of text and formats that file so that it fits in a specific width.To format this file, the user specifies the input file, the length of an output line, and the justification for the output text. An example would be this:
$ ./format test.dat 15 right
The quick brown
fox jumps over
the lazy old
dog.
$ ./format test.dat 15 left
The quick brown
fox jumps over
the lazy old
dog.
$ ./format test.dat 15 center
The quick brown
fox jumps over
the lazy old
dog.
Anyway, I am basically stuck on how to go about outputting the file based on this. Attached below is my code for reading in the file, and what little I have done on outputting the file. I am mainly looking for tips or suggestions on how to go about doing it. I know I need to use printf with a width and such, but I am confused on how to move to the next line.
char **inputFile(FILE *fp, int size) {
int i = 0;
char *token;
char **str;
str = malloc(sizeof(char *) * size);
token = readToken(fp);
while(!feof(fp)) {
if(i+1 == size) {
realloc(str, size * 2);
}
str[i] = token;
token = readToken(fp);
i++;
}
return str;
}
void toPrint(char **string, int width, int indent) {
int i;
int curLineLength;
if(indent == 0) {
for(i = 0; I < strlen(string); i++
char *token = string[i];
if(curLineLength + strlen(*string) > width) {
if(curLineLength > 0) {
printf("\n");
curLineLength = 0;
}
}
printf("%s ", token);
curLineLength += strlen(*string);
}
/*
if(indent == 1) {
}
if(indent == 2) {
}
*/
}
Following on from the comment, your task of justifying the lines is more a logic challenge for structuring your output function than it is a difficult one. You are getting close. There are a number of ways to do it, and you probably need to add error checking to make sure width isn't less than the longest line.
Here is a quick example you can draw from. Make sure you understand why each line was written the way it was, and pay close attention to the variable length array definition (you will need to compile as c99 - or if using gcc, rely on the gcc extension (default)). Let me know if you have questions:
/* format 'n' lines of output of 't' justified as specified in 'just'
(left 'l', right 'r' or centered 'c') within a width 'w'.
NOTE: 'w' must be larger than the longest line in 't'.
*/
void formatted (char **t, char just, size_t w, size_t n)
{
if (!t || !*t) return;
size_t i = 0;
size_t lmax = 0;
size_t len[n];
/* calculate the length of each line, set lmax */
for (i = 0; i < n; i++) {
len[i] = strlen (t[i]);
if (len[i] > lmax) lmax = len[i];
}
/* handle w < lmax reformat or error here */
if (w < lmax) {
fprintf (stderr, "%s() error: invalid width < lmax (%zu).\n",
__func__, lmax);
return;
}
/* left justified output */
if (just == 'l') {
for (i = 0; i < n; i++) {
printf ("%s\n", t[i]);
}
return;
}
/* center or right justified output */
for (i = 0; i < n; i++) {
int spaces = w - len[i];
if (just == 'c')
printf ("%*s%s\n", spaces/2, " ", t[i]);
else if (just == 'r')
printf ("%*s%s\n", spaces, " ", t[i]);
}
}
Note: if you are on windows, change __func__ to the function name in each of the error statements.
Logic of Function - Long Version
Let's look a little closer at what the function is doing and why it does it the way it does. First, lets look at the paramaters it takes:
void formatted (char **t, char just, size_t w, size_t n)
char **t, your 'string', well... actually your array or pointers to type char*. When you pass the array of pointers to your function, and this may be where your confusion is, you only have 2 ways to iterate over the array an print each of the lines of text: (1) pass the number of valid pointers that point to strings containing text, or (2) provide a sentinel within the array that is pointed to by the pointer following the last pointer that points to a valid line of text. (usually just NULL) The sentinel serves as the pointer in the array that tells you ("Hey dummy, stop trying to print lines -- you already printed the last one...")
This takes a little more explanation. Consider your array of pointers in memory. Normally you will always allocate some reasonably anticipated number of pointers to fill, and when you fill the last pointer, you will realloc the array to contain more space. What this means is you will always have at least 1 pointer at the end of your array that is not filled. If you will intialize your array to contain NULL pointers at the very beginning (by allocating with calloc instead of malloc) -- you automatically provide a sentinel, or, you can always explicitly set the next pointer to NULL as you fill your array. This will leave your array of pointers to char* looking similar to the following:
Pointer The pointers in the array of pointers to char*, char **t;
Address point to the first character in each associated string.
+-----------+
| 0x192a460 | --> The quick brown
+-----------+
| 0x192a480 | --> fox jumps over
+-----------+
| 0x192a4a0 | --> a lazy
+-----------+
| 0x192a4c0 | --> dog.
+-----------+
| NULL |
+-----------+
| NULL |
+-----------+
...
Understand: your string, my t is an array of pointers to type char* not the strings themselves. Your string is the left column of pointers above. string is an array of the starting addresses of each real string and your string[i] will be the start of actual string itself -- at that address. i will range from 0-3 (4 total) where string[0] = "The quick brown", string[1] = "fox jumps over", etc.. To help, change the name in your code of string to array or str_array to help keep this straight.
Your 2 options for iterating over the array to print each string become (1) (with size 'n' passed to function):
for (i = 0; i < n; i++)
printf ("%s\n", t[i]);
or (2) (relying on a sentinel):
while (t[i]))
printf ("%s\n", t[i++]);
(while not readily apparent here, this provides significant benefits as your code becomes more complex and you need to pass the array between many different functions)
Now that you know how you can access each of your strings, start with the case where you will just print the string left justified. You don't care about the length and you don't care about the width (as long as your string will fit within the width). All you need to do is print each of the strings. Since we pass the number of strings 'n' to the funciton, we can simply use the for loop (method 1) to print each string. (the length is computed prior to printing to insure each string will fit in width, since we will use it later, we store each length in the variable length array len so we don't have to make redudant calls to strlen later)
The more interesting cases are the center and right justified cases. In addition to 'n' you need to know which justification the user wants. You can pass any type flag you want to pass that information to the function. A char (1-byte) is simply the most efficient and does not require conversion to a number when read as input to the program. That is essentially why I chose to pass just as a char instead of an int (4-bytes + conversion on input) (or short (2-bytes), etc..)
Let's first look at how we right-justify the output. For discussion, let's consider an output width of 20 characters you want to right-justify your strings in. Your first string is 15 (printable) characters long (it's actually 16 chars in memory due to the null-terminating char at the end). Let's visualize what our string of printable characters would look like in a 20-character buffer (which you would use to save a right-justified copy of the string in memory, rather than printing)
|<------- 20 character width -------->|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | |T|h|e| |q|u|i|c|k| |b|r|o|w|n|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|<-- 15 character length -->|
This makes it easy to see how many spaces are needed before we start printing our string. The task here is to turn this into a generalized piece of code to handle any length string. Not too difficult:
spaces = width - length;
Then to utilize this to print the string right-justified, we make use of the minimum field width directive in the printf format string. Of particular use here, is the ability to specify the field width directive as a variable in the printf argument list. Constructing the format string and argument list to accomplish our goal, we have:
printf ("%*s%s\n", spaces, " ", t[i]);
Which says, print a minimum field width of spaces for the string " " (which essentially prints spaces number of spaces -- poor choice of names) followed by the actual string in our array of pointers t[i].
Looking at the diagram again, what would we have to do to shift the string to the center of the 20-character width? Instead of shifing the whole width - length number of spaces, we could only shift it 1/2 that much and it would end up where we want it. (I can hear the gears in your head grinding, and I can smell the smoke -- "but, wait... 5 is an odd number and we are using integers!" -- it doesn't matter, integer division will take care of it, and if we shift by 2 instead of 2.5, it's just fine, you can't print 1/2 a character... So putting it all together, to handle centered or right justified text, all you need is:
for (i = 0; i < n; i++) {
int spaces = w - len[i];
if (just == 'c')
printf ("%*s%s\n", spaces/2, " ", t[i]);
else if (just == 'r')
printf ("%*s%s\n", spaces, " ", t[i]);
}
Putting It All Together With The Rest
Sometimes seeing how the whole thing fits together helps. Same rules. Go though it function-by-function, line-by-line, and ask questions when you get struck. C is a low-level language, meaning you have to understand where things are in memory. When it comes down to it, programming is really about how to manipulate what you load into memory. Other languages try to hide that from you. C doesn't. That's its strength, and also where you have to concentrate a good part of your learning. Enough babble, here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 256
#define MAXL 64
char **inputfile (char ***t, FILE *fp, size_t *sz);
void formatted (char **t, char just, size_t w, size_t n);
void *xcalloc (size_t n, size_t s);
void freecdp (char **p, size_t n);
unsigned chr2lower (unsigned c);
int main (int argc, char **argv) {
if (argc < 4) { /* validate required arguments on command line */
fprintf (stderr, "error: insufficient input. "
"usage: %s just width [filename (stdin)]\n",
argv[0]);
return 1;
}
char **text = NULL; /* initialize all variables for main */
size_t lines = 0;
char j = 0; /* the following are ternary operators */
char just = argc > 1 ? *argv[1] : 'l'; /* sets defaults if no input */
size_t width = argc > 2 ? (size_t)strtol (argv[2], NULL, 10) : 0;
FILE *fp = argc > 3 ? fopen (argv[3], "r") : stdin;
/* read input from file */
if (!(inputfile (&text, fp, &lines))) { /* check if return is NULL */
fprintf (stderr, "error: file read failed '%s'.\n",
argc > 3 ? argv[3] : "stdin");
return 1;
}
j = chr2lower (just); /* force user input to lower-case */
if (j != 'l' && j != 'r' && j != 'c')
fprintf (stderr, "error: invalid justification '%c' "
"(defaulting to 'left').\n", just);
/* print output in requested justification */
formatted (text, j, width, lines);
/* free all memory allocated in program */
freecdp (text, lines);
return 0;
}
char **inputfile (char ***t, FILE *fp, size_t *sz)
{
if (!t || !sz) { /* validate parameters are not NULL */
fprintf (stderr, "%s() error: invalid parameters.\n", __func__);
return NULL;
}
if (!fp) { /* check that file pointer is valid */
fprintf (stderr, "%s() error: file open failed.\n", __func__);
return NULL;
}
size_t idx = 0; /* declare/initialize function variables */
size_t maxl = MAXL;
char ln[MAXC] = {0};
/* allocate MAXL number of pointers */
*t = xcalloc (MAXL, sizeof **t);
while (fgets (ln, MAXC, fp)) { /* read each line in file */
size_t len = strlen (ln); /* calculate length */
/* remove trailing newline (or carriage return) */
while (len && (ln[len-1] == '\n' || ln[len-1] == '\r'))
ln[--len] = 0;
/* allocate & copy ln saving pointer in t[i], increment i by 1 */
(*t)[idx++] = strdup (ln); /* strdup allocates & copies */
if (idx == maxl) { /* check if you reached limit, realloc if needed */
void *tmp = realloc (*t, maxl * sizeof **t * 2);
if (!tmp) {
fprintf (stderr, "%s() virtual memory exhausted.\n", __func__);
return NULL;
}
*t = tmp; /* set new pointers NULL below (sentinel) */
memset (*t + maxl, 0, maxl * sizeof **t);
maxl *= 2;
}
}
*sz = idx; /* update value at address of sz so it is available in main */
if (fp != stdin) fclose (fp);
return *t;
}
/* format 'n' lines of output of 't' justified as specified in 'just'
(left 'l', right 'r' or centered 'c') within a width 'w'.
NOTE: 'w' must be larger than the longest line in 't'.
*/
void formatted (char **t, char just, size_t w, size_t n)
{
if (!t || !*t) return;
size_t i = 0;
size_t lmax = 0;
size_t len[n];
/* calculate the length of each line, set lmax */
for (i = 0; i < n; i++) {
len[i] = strlen (t[i]);
if (len[i] > lmax) lmax = len[i];
}
/* handle w < lmax reformat or error here */
if (w < lmax) {
fprintf (stderr, "%s() error: invalid width < lmax (%zu).\n",
__func__, lmax);
return;
}
/* left justified output */
if (just == 'l') {
for (i = 0; i < n; i++) {
printf ("%s\n", t[i]);
}
return;
}
/* center or right justified output */
for (i = 0; i < n; i++) {
int spaces = w - len[i];
if (just == 'c')
printf ("%*s%s\n", spaces/2, " ", t[i]);
else if (just == 'r')
printf ("%*s%s\n", spaces, " ", t[i]);
}
}
/* help functions below for calloc, free, and to lower-case */
void *xcalloc (size_t n, size_t s)
{
register void *memptr = calloc (n, s);
if (memptr == 0) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n",
__func__);
exit (EXIT_FAILURE);
}
return memptr;
}
void freecdp (char **p, size_t n)
{
if (!p) return;
size_t i;
for (i = 0; i < n; i++)
free (p[i]);
free (p);
}
unsigned chr2lower (unsigned c)
{ return ('A' <= c && c <= 'Z') ? c | 32 : c; }
Example Use/Output
$ ./bin/str_justify l 15 dat/fox_4lines.txt
The quick brown
fox jumps over
a lazy
dog.
$ ./bin/str_justify c 15 dat/fox_4lines.txt
The quick brown
fox jumps over
a lazy
dog.
$ ./bin/str_justify r 15 dat/fox_4lines.txt
The quick brown
fox jumps over
a lazy
dog.
this is my code.
the input numbers are
1234567890
the output of this code should be
(123)456-7890
but the output is different. Any advice or error fixes in my code?
#include <stdio.h>
#include <ctype.h>
int main()
{
char ch;
int a[100], s[100], str, k, i;
FILE *fp;
fp = fopen("number.c", "r");
while ( ( ch = fgetc(fp) ) != EOF )
{
k = 0;
a[k] = '(';
a[k+4] = ')';
a[k+8] = '-';
for (i = 0; s[i] != '\0'; i++)
{
if (isdigit(s[i]))
{
a[k++] = s[i];
if (k == 3)
{
k++;
}
}
printf("%s", a);
}
fclose(fp);
return 0;
}
}
This looks like an assignment from a first year course in CS. If so, I would say find a TA during office hours and discuss.
There are several issues with the code:
Your outer loop is intending to read a line at a time from a file and populate the s array. It is instead reading a character at a time and populating the ch variable.
As mentioned in the comments, you are not accounting for the "-" when putting characters into the a array.
You are not terminating your string in the a array.
There may be different schools of thought on this in c, but I would make s and a char[] instead of int[].
My advice would be to get out a piece of paper and make spaces for each of your variables. Then read your code line by line and manipulate your variables the way you expect the computer to execute what is written. If you can read what is written, rather than what you expect the code to do, then the issues will become apparent.
/* ugly: The old phone #
nice: The formatted phone #
*/
#include <stdio.h>
void fmtpn(const char *ugly, char *nice)
{
int i, j;
/* add one to allocate space for null terminator */
char first3[3 + 1], next3[3 + 1], last4[4 + 1];
if (strlen(ugly) != 10 || containsalpha(ugly)) {
strcpy(nice, "Invalid pn!");
return;
}
for (i = 0; i < 3; ++i)
first3[i] = ugly[i];
first3[i] = 0; /* null terminate the string */
for (j = 0; j < 3; ++i, ++j)
next3[j] = ugly[i];
next3[j] = 0; /* null terminate */
for (j = 0; j < 4; ++i, ++j)
last4[j] = ugly[i];
last4[j] = 0; /* null terminate */
sprintf(nice, "(%s) %s-%s", first3, next3, last4);
}
To read from the file:
FILE *fp;
char ugly[32], good[32];
if (fp = fopen("file", "r")) {
fgets(ugly, 32, fp);
fmtpn(ugly, good);
puts(good);
}
No love for sscanf?
#include <stdio.h>
int prettyprint(char *input, char *output)
{
int n[10], ret;
ret = sscanf(input, "%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d", &(n[0]), &(n[1]),
&(n[2]), &(n[3]), &(n[4]), &(n[5]), &(n[6]),
&(n[7]), &(n[8]), &(n[9]));
if (ret != 10)
fprintf(stderr, "invalid input\n");
sprintf(output, "(%1d%1d%1d) %1d%1d%1d-%1d%1d%1d%1d",
n[0], n[1], n[2],
n[3],n[4], n[5],
n[6], n[7], n[8], n[9]);
return 0;
}
int main(int argc, char **argv)
{
char digits[] = "0123456789";
char output[256];
prettyprint(digits, output);
printf("%s\n", output);
}
You have other options aside from looping through your sting to build the phone number. Sometimes, when dealing with fixed strings or known quantities, a straight forward packing of the characters into a fixed format is a lot simpler than picking the characters out of loops.
For example, here you know you are dealing with a 10 char string of digits. In your code you can read/parse each line into a string of 10 digits. Then your only task is to format those 10 digits into the phone number. Using a pointer for each string and then strncpy is about as easy as anything else:
#include <stdio.h>
#include <string.h>
int main (void) {
char *digits = "1234567890";
char *p = digits;
char phonenum[15] = {0};
char *pf = phonenum;
/* build formatted phone number */
*pf++ = '(';
strncpy (pf, p, 3);
pf += 3, p += 3;
*pf++ = ')';
*pf++ = ' '; /* note: included space, remove line if unwanted */
strncpy (pf, p, 3);
pf += 3, p += 3;
*pf++ = '-';
strncpy (pf, p, 4);
pf += 4;
*pf = 0;
printf ("\n digits : %s\n phone : %s\n\n", digits, phonenum);
return 0;
}
Output
$ ./bin/phnumbld
digits : 1234567890
phone : (123) 456-7890
You can easily turn the code above into a simple function that creates a formatted phone number given any 10-digit string. Breaking your code down into functional pieces not only makes your code easier to read and write, but it also builds flexibility and ease of maintenance into your code. Here were you dealing with an actual dial-string that included the international dialing prefix and country code, you could easily format the last 10 digits of the longer string by using a pointer to the appropriate beginning character.
With File Handling
Writing anything in C is no different. You simply break the problem down into discrete operations and then write small bits of code to handle each part of the problem. As you get more experience, you will build a collection of routines to handle most situations.
Below the code declare three constants. ACPN (area code phone number length), MAXC (maximum digits in dial string including country code and international dialing prefix), and MAXS (maximum number of chars in line to read from file)
You options for reading lines of data in C are broken into two broad categories, character oriented input and line oriented input. When reading lines from a file, in most cases line oriented input is the proper choice. You read a line of data at a time into a buffer, then you parse the information you need from the buffer. Your primary choices for line oriented input in C are fgets and getline. We use the standard fgets below.
Below, the code will read a line of data, then call get_n_digits to extract up to MAXC digits in the line into a separate buffer holding the digits (numstr). The number string is then passed to fmt_phone which takes the last 10 digits in the string (discarding any initial country-code or int'l dialing prefix) and formatting those digits into a telephone number format. You can adjust any part as needed to meet your input file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ACPN 10
#define MAXC 16
#define MAXS 256
size_t strip_newline (char *s);
char *get_n_digits (char *numstr, char *s, size_t n);
char *fmt_phone (char *fmts, char *s, size_t n);
int main (int argc, char **argv) {
/* open file or read from stdin */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: failed to open file for reading\n");
return 1;
}
char numstr[MAXC] = {0}; /* string of digits (max MAXC - 1) */
char fmtstr[MAXC] = {0}; /* formatted phone number string */
char line[MAXS] = {0}; /* line buffer holding full line */
/* read each line from fp (up to MAXS chars) */
while (fgets (line, MAXS, fp))
{
size_t len = strip_newline (line); /* strip trailing newline */
get_n_digits (numstr, line, MAXC); /* get MAXC digits from line */
printf ("\n read : %s (%zu chars), taking last 10 of : %s\n",
line, len, numstr);
/* format last 10 digits into phone number */
fmt_phone (fmtstr, numstr, ACPN);
printf (" phone : %s\n", fmtstr);
}
if (fp != stdin) fclose (fp);
return 0;
}
size_t strip_newline (char *s)
{
size_t len = strlen (s);
s [--len] = 0;
return len;
}
/* extract upto n digits from string s, copy to numstr */
char *get_n_digits (char *numstr, char *s, size_t n)
{
char *p = s;
size_t idx = 0;
while (*p && idx < n - 1) {
if (*p >= '0' && *p <= '9')
numstr[idx++] = *p;
p++;
}
numstr[idx] = 0;
return numstr;
}
/* format last n (10) digits in s into a formatted
telephone number: (xxx) yyy-zzzz, copy to fmts.
'last 10' accounts for country code and international
dialing prefix at beginning of dial string.
*/
char *fmt_phone (char *fmts, char *s, size_t n)
{
/* validate strings */
if (!fmts || !s) {
fprintf (stderr, "%s() error: invalid string parameter.\n", __func__);
*fmts = 0;
return fmts;
}
/* validate length of n */
if (n < ACPN) {
fprintf (stderr, "%s() error: insufficient size 'n' for format.\n", __func__);
*fmts = 0;
return fmts;
}
/* validate length of s */
size_t len = strlen (s);
if (len < n) {
fprintf (stderr, "%s() error: insufficient digits in string.\n", __func__);
*fmts = 0;
return fmts;
}
/* set start pointer to last 10 digits */
char *p = len > n ? s + len - n : s;
char *pf = fmts;
/* build formatted phone number */
*pf++ = '(';
strncpy (pf, p, 3);
pf += 3, p += 3;
*pf++ = ')';
*pf++ = ' ';
strncpy (pf, p, 3);
pf += 3, p += 3;
*pf++ = '-';
strncpy (pf, p, 4);
pf += 4;
*pf = 0;
return fmts;
}
Compile with gcc -Wall -Wextra -o progname sourcename.c
Example Input
$ cat dat/pnumtest.txt
123456789012345
12345678901234
1234567890123
123456789012
12345678901
1234567890
123456789
Example Output
$ ./bin/phnum dat/pnumtest.txt
read : 123456789012345 (15 chars), taking last 10 of : 123456789012345
phone : (678) 901-2345
read : 12345678901234 (14 chars), taking last 10 of : 12345678901234
phone : (567) 890-1234
read : 1234567890123 (13 chars), taking last 10 of : 1234567890123
phone : (456) 789-0123
read : 123456789012 (12 chars), taking last 10 of : 123456789012
phone : (345) 678-9012
read : 12345678901 (11 chars), taking last 10 of : 12345678901
phone : (234) 567-8901
read : 1234567890 (10 chars), taking last 10 of : 1234567890
phone : (123) 456-7890
read : 123456789 (9 chars), taking last 10 of : 123456789
fmt_phone() error: insufficient digits in string.
phone :
Note: there are many, many different ways to approach this problem, this is but one.
Note2: while not required for this code, I included a function showing how to strip the trailing newline ('\n') from the input read by fgets. It is never a good idea to leave newlines dangling from strings in your code. While here they would not have caused a problem, in most cases they will bite you if your are not aware of them. So get in the practice of handling/removing the trailing newlines when using fgets or getline to read from a file. (note: getline provides the number of characters actually read as its return, so you can avoid calling strlen and simply use the return of getline to remove the newline in that case.)