I am trying to read lines from a text file of unknown length.
In the line there can be leading and trailing white-spaces until the string occurs.
So my first step is to read line by line and allocate memory for the strings. Then remove all the leading and trailing white spaces.
After that I want to check if the string has any white space characters in it which is an invalid character. For example the string can not look like this "bad string" but can look like this "goodstring".
However when I call the function to remove the leading and trailing white spaces it also removes characters before or after a white space.
Could someone tell me what I am doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define NCHAR 64
char *readline (FILE *fp, char **buffer);
char *strstrip(char *s);
int main (int argc, char **argv) {
char *line = NULL;
size_t idx = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (readline (fp, &line)) { /* read each line in 'fp' */
printf (" line[%2zu] : %s\n", idx++, line);
free (line);
line = NULL;
}
if (fp != stdin) fclose (fp);
return 0;
}
/* read line from 'fp' allocate *buffer NCHAR in size
* realloc as necessary. Returns a pointer to *buffer
* on success, NULL otherwise.
*/
char *readline (FILE *fp, char **buffer)
{
int ch;
size_t buflen = 0, nchar = NCHAR;
size_t n;
char *invalid_character = " ";
*buffer = malloc (nchar); /* allocate buffer nchar in length */
if (!*buffer) {
fprintf (stderr, "readline() error: virtual memory exhausted.\n");
return NULL;
}
while ((ch = fgetc(fp)) != '\n' && ch != EOF)
{
(*buffer)[buflen++] = ch;
if (buflen + 1 >= nchar) { /* realloc */
char *tmp = realloc (*buffer, nchar * 2);
if (!tmp) {
fprintf (stderr, "error: realloc failed, "
"returning partial buffer.\n");
(*buffer)[buflen] = 0;
return *buffer;
}
*buffer = tmp;
nchar *= 2;
}
strstrip(*buffer); //remove traiing/leading spaces
}
(*buffer)[buflen] = 0; /* nul-terminate */
if (invalid_character[n = strspn(invalid_character, *buffer)] == '\0') //check if a string has invalid character ' ' in it
{
puts(" invalid characters");
}
if (buflen == 0 && ch == EOF) { /* return NULL if nothing read */
free (*buffer);
*buffer = NULL;
}
return *buffer;
}
char *strstrip(char *s)
{
size_t size;
char *end;
size = strlen(s);
if (!size)
return s;
end = s + size - 1;
while (end >= s && isspace(*end))
end--;
*(end + 1) = '\0';
while (*s && isspace(*s))
s++;
return s;
}
You do not need to worry about the length of the string passed to strstrip(), simply iterate over all characters in the string removing whitespace characters, e.g. the following version removals ALL whitespace from s:
/** remove ALL leading, interleaved and trailing whitespace, in place.
* the original start address is preserved but due to reindexing,
* the contents of the original are not preserved. returns pointer
* to 's'. (ctype.h required)
*/
char *strstrip (char *s)
{
if (!s) return NULL; /* valdiate string not NULL */
if (!*s) return s; /* handle empty string */
char *p = s, *wp = s; /* pointer and write-pointer */
while (*p) { /* loop over each character */
while (isspace ((unsigned char)*p)) /* if whitespace advance ptr */
p++;
*wp++ = *p; /* use non-ws char */
if (*p)
p++;
}
*wp = 0; /* nul-terminate */
return s;
}
(note: if the argument to isspace() is type char, a cast to unsigned char is required, see NOTES Section, e.g. man 3 isalpha)
Removing only Excess Whitespace
The following version removes leading and trailing whitespace and collapses multiple sequences of whitespace to a single space:
/** remove excess leading, interleaved and trailing whitespace, in place.
* the original start address is preserved but due to reindexing,
* the contents of the original are not preserved. returns pointer
* to 's'. (ctype.h required) NOTE: LATEST
*/
char *strstrip (char *s)
{
if (!s) return NULL; /* valdiate string not NULL */
if (!*s) return s; /* handle empty string */
char *p = s, *wp = s; /* pointer and write-pointer */
while (*p) {
if (isspace((unsigned char)*p)) { /* test for ws */
if (wp > s) /* ignore leading ws, while */
*wp++ = *p; /* preserving 1 between words */
while (*p && isspace (unsigned char)(*p)) /* skip remainder */
p++;
if (!*p) /* bail on end-of-string */
break;
}
if (*p == '.') /* handle space between word and '.' */
while (wp > s && isspace ((unsigned char)*(wp - 1)))
wp--;
*wp++ = *p; /* use non-ws char */
p++;
}
while (wp > s && isspace ((unsigned char)*(wp - 1))) /* trim trailing ws */
wp--;
*wp = 0; /* nul-terminate */
return s;
}
(note: s must be mutable and therefore cannot be a string-literal)
Related
So what i have is a string(str) that i get from fgets(str, x, stdin);.
If i write for example "Hello World" i want to be able to add a character infront of each word in the string.
To get this "Hello? World?" as an example. I think i've made it alot harder for myself by trying to solve it this way:
add(char *s, char o, char c){
int i, j = 0;
for (i = 0; s[i] != '\0'; i++) {
if (s[i] != o) {
s[j] = s[i];
}
else {
s[j] = c;
}
j++;
}
}
add(str, ' ','?');
printf("\n%s", str);
This will read out "Hello?World" without the spaces. Now the only way i see this working is if i move everything after the first "?" one to the right while also making the positon of the "W" to a space and a "?" at the end. But for much longer strings i can't see myself doing that.
You can't safely extend a string with more characters without insuring the buffer that holds the string is big enough. So let's devise a solution that counts how many additional characters are needed, allocate a buffer big enough to hold a string of that length, then do the copy loop. Then return the new string back to the caller.
char* add(const char* s, char o, char c)
{
size_t len = strlen(s);
const char* str = s;
char* result = NULL;
char* newstring = NULL;
// count how many characters are needed for the new string
while (*str)
{
len += (*str== o) ? 2 : 1;
str++;
}
// allocate a result buffer big enough to hold the new string
result = malloc(len + 1); // +1 for null char
// now copy the string and insert the "c" parameter whenever "o" is seen
newstring = result;
str = s;
while (*str)
{
*newstring++ = *str;
if (*str == o)
{
*newstring++ = c;
}
str++;
}
*newString = '\0';
return result;
}
Then your code to invoke is as follows:
char* newstring g= add(str, ' ','?');
printf("\n%s", newstring);
free(newstring);
#include <stdio.h>
#include <string.h>
int main(void) {
char text[] = "Hello World";
for(char* word = strtok(text, " .,?!"); word; word = strtok(NULL, " .,?!"))
printf("%s? ", word);
return 0;
}
Example Output
Success #stdin #stdout 0s 4228KB
Hello? World?
IDEOne Link
Knowing the amount of storage available when you reach a position where the new character will be inserted, you can check whether the new character will fit in the available storage, move from the current character through end-of-string to the right by one and insert the new character, e.g.
#include <stdio.h>
#include <string.h>
#define MAXC 1024
char *add (char *s, const char find, const char replace)
{
char *p = s; /* pointer to string */
while (*p) { /* for each char */
if (*p == find) {
size_t remain = strlen (p); /* get remaining length */
if ((p - s + remain < MAXC - 1)) { /* if space remains for char */
memmove (p + 1, p, remain + 1); /* move chars to right by 1 */
*p++ = replace; /* replace char, advance ptr */
}
else { /* warn if string full */
fputs ("error: replacement will exceed storage.\n", stderr);
break;
}
}
p++; /* advance to next char */
}
return s; /* return pointer to beginning of string */
}
...
(note: the string must be mutable, not a string-literal, and have additional storage for the inserted character. If you need to pass a string-literal or you have no additional storage in the current string, make a copy as shown by #Selbie in his answer)
Putting together a short example with a 1024-char buffer for storage, you can do something like:
#include <stdio.h>
#include <string.h>
#define MAXC 1024
char *add (char *s, const char find, const char replace)
{
char *p = s; /* pointer to string */
while (*p) { /* for each char */
if (*p == find) {
size_t remain = strlen (p); /* get remaining length */
if ((p - s + remain < MAXC - 1)) { /* if space remains for char */
memmove (p + 1, p, remain + 1); /* move chars to right by 1 */
*p++ = replace; /* replace char, advance ptr */
}
else { /* warn if string full */
fputs ("error: replacement will exceed storage.\n", stderr);
break;
}
}
p++; /* advance to next char */
}
return s; /* return pointer to beginning of string */
}
int main (void) {
char buf[MAXC];
if (!fgets (buf, MAXC, stdin))
return 1;
buf[strcspn(buf, "\n")] = 0;
puts (add (buf, ' ', '?'));
}
Example Use/Output
$ ./bin/str_replace_c
Hello World?
Hello? World?
Look things over and let me know if you have questions.
Just for fun, here's my implementation. It modifies the string in-place and in O(n) time. It assumes that the char-buffer is large enough to hold the additional characters, so it's up to the calling code to ensure that.
#include <stdio.h>
void add(char *s, char o, char c)
{
int num_words = 0;
char * p = s;
while(*p) if (*p++ == o) num_words++;
char * readFrom = p;
char * writeTo = p+num_words;
char * nulByte = writeTo;
// Insert c-chars, iterating backwards to avoid overwriting chars we have yet to read
while(readFrom >= s)
{
*writeTo = *readFrom;
if (*writeTo == o)
{
--writeTo;
*writeTo = c;
}
writeTo--;
readFrom--;
}
// If our string doesn't end in a 'c' char, append one
if ((nulByte > s)&&(*(nulByte-1) != c))
{
*nulByte++ = c;
*nulByte = '\0';
}
}
int main(int argc, char ** argv)
{
char test_string[1000] = "Hello World";
add(test_string, ' ','?');
printf("%s\n", test_string);
return 0;
}
The program's output is:
$ ./a.out
Hello? World?
I'm trying to get the last word of a string as an argv parameter in C. How can I find the last word and delimit the strings that contain a space/tabs?
The definition of a word is "a section of string delimited by spaces/tabs or by the start/end of the string." I initially made a function to identify it is whitespace or tab; however, the solution I have does not for all cases. Most of it works, but when my argv has a string like "asdkBakjsdhf1785 ", the expected output should be "asdkBakjsdhf1785", not "asdkBakjsdhf1785 ". All of these functions has to made from scratch and I'm only allowed to use the write function from the <unistd.h> library.
int my_isspace(char c)
{
if (c == ' ' || c == '\t')
return (1);
return (0);
}
void my_putstr(char *str)
{
while (*str)
write(1, str++, 1);
}
void last_word(char *str)
{
char *last;
int i;
i = 0;
last = &str[i];
while (str[i])
{
if (my_isspace(str[i]))
{
if (str[i + 1] >= '!' && str[i + 1] <= '~')
last = &str[i + 1];
}
i++;
}
if (last)
my_putstr(last);
}
int main(int argc, char *argv[])
{
if (argc == 2)
last_word(argv[1]);
my_putstr("\n");
return (0);
}
For now, what it does is that it parses through the string and checks if it has a whitespace/tab. If it has, it will ignore it and move to the next character until it hits a character between ! and ~ (including those). Once it hits to any of the characters in between, it will store the parsing into another empty string pointer to preserve what was the "last word". With the way that I'm using, where can I improve the tiny bit to delimit my space/tab at the end?
You have a couple of problems. First it would help to make your my_isspace() roughly equivalent to isspace() so that it correctly considers line-ends as whitespace as well, e.g.
int my_isspace (const char c)
{
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
return 1;
return 0;
}
Next, you can make last_word() (and many other similar classification functions) much simpler by taking a State Loop approach and keeping a simple flag to help you with your classification. For instance here, it helps to know whether you are reading within a word or not. That lends itself to a simple in/out state, which you can track with a simple flag of int in = 0; for outside a word, and in = 1; when within a word.
Putting that in use, your last_word() can be written as follows for mutable strings (the arguments to main() being mutable):
void last_word (char *str)
{
char *p = NULL, /* your last */
*ep = NULL; /* end pointer to end of last */
int in = 0; /* flag - in/out of a word */
while (*str) { /* loop over each char */
if (my_isspace (*str)) { /* if a space */
if (in) /* if in a word */
ep = str; /* set endptr to current char */
in = 0; /* set out of word */
}
else { /* not a space */
if (!in) /* if not in a word */
p = str; /* set start pointer */
in = 1; /* set in word */
}
str++; /* increment pointer */
}
if (p) { /* if we have start of word */
if (ep) /* if we have endptr set */
*ep = 0; /* nul-terminate at endptr */
my_putstr (p); /* output last */
}
}
(note: if dealing with non-mutable string input, you could use ep - p to get the number of characters to output and output them one at a time)
A complete example (with a few cleanups) could be:
#include <unistd.h>
int my_isspace (const char c)
{
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
return 1;
return 0;
}
void my_putstr (const char *str)
{
size_t n = 0;
for (n = 0; str[n]; n++) {}
write (1, str, n);
}
void last_word (char *str)
{
char *p = NULL, /* your last */
*ep = NULL; /* end pointer to end of last */
int in = 0; /* flag - in/out of a word */
while (*str) { /* loop over each char */
if (my_isspace (*str)) { /* if a space */
if (in) /* if in a word */
ep = str; /* set endptr to current char */
in = 0; /* set out of word */
}
else { /* not a space */
if (!in) /* if not in a word */
p = str; /* set start pointer */
in = 1; /* set in word */
}
str++; /* increment pointer */
}
if (p) { /* if we have start of word */
if (ep) /* if we have endptr set */
*ep = 0; /* nul-terminate at endptr */
my_putstr (p); /* output last */
}
}
int main (int argc, char **argv) {
my_putstr ("'");
if (argc >= 2)
last_word (argv[1]);
my_putstr ("'");
my_putstr ("\n");
return 0;
}
Example Use/Output
Single word:
$ ./bin/write_last_argv1 "fleas"
'fleas'
Single word with trailing whitespace:
$ ./bin/write_last_argv1 "fleas "
'fleas'
Multiple words:
$ ./bin/write_last_argv1 "my dog has fleas"
'fleas'
Multiple words with leading, trailing and multiple intervening whitespace:
$ ./bin/write_last_argv1 " my dog has fleas "
'fleas'
Look things over and let me know if you have further questions.
I'm writing a program that is supposed to search for a name in a CSV file and copy the record (all the info on the same line as the name) that goes along with it.
For example, if CSV file contains:
Bob, 13, 12345612
Eli, 12, 21398743
I would input "Bob" to get the first line, and copy this into an array called "record".
So far my code is as follows:
#include<stdio.h>
#include<stdlib.h>
void FindRecord(char *a, char *b, char c[]);
void main(void){
char arrayName[100];
char arrayNewname[100];
char *name = arrayName;
char *newname = arrayNewname;
char record[1000];
printf("Please input a name in the phonebook: ");
scanf("%s", name);
printf("Please input a replacement name: ");
scanf("%s", newname);
FindRecord("phonebook.csv",name,record);
}
void FindRecord(char *filename, char *name, char record[]){
//Create temp array of max size
char temp[1000];
//Open file
FILE *f = fopen(filename, "r");
//Make sure file exists
if (f == NULL){
printf("File does not exist");
fclose(f);
exit(1);
}
//While
while(!feof(f)){
//Read one line at a time
fgets(temp, 1000, f);
int i = 0;
int *p;
for(int i = 0; i < 1000; i++){
if(temp[i] == *name){
record[i] = temp[i];
name++;
}
size_t n = (sizeof record /sizeof record[0]);
if(temp[i] == *name){
*p = temp[i + n];
}
}
}
printf("%s", record);
fclose(f);
}
Basically, I've found Bob and copied Bob, but do not understand how to proceed using pointers (not allowed to use string.h) and copying the rest of the line. I've been trying to play around with the length of the word once Ive found it but this isn't working either because of pointers. Any help/hints would be appreciated.
When you are not allowed to use string.h you have to compare strings char by char.
Start by having a loop to find the name.
See: Compare two strings character by character in C
And then copy the rest of the record. Either char by char or with more advance functions like memcpy:
https://www.techonthenet.com/c_language/standard_library_functions/string_h/memcpy.php
Now who is int *p ?
You didn't initialized this pointer. You have to either malloc it or assign it to an existing memory.
See for more information:
https://pebble.gitbooks.io/learning-c-with-pebble/content/chapter08.html
I think you should read more about pointers and then things will work much better for you.
Not being allowed to use string.h is a good exercise in computing the string lengths manually (necessary to remove the trailing '\n' from the lines read with fgets) and also a good exercise for manual string comparisons.
For example, if you are reading lines into buf, you can use a simple for loop to get the length of buf, e.g.
int blen = 0;
for (; buf[blen]; blen++) {} /* get line length */
(note: you find the length of name, say nlen in a similar manner)
Then having the length in blen, you can easily check that the final character in buf is the '\n' character and remove it by overwriting the newline with the nul-terminating character, e.g.
if (blen && buf[blen - 1] == '\n') /* check/remove '\n' */
buf[--blen] = 0; /* overwrite with '\0' */
The remainder of your findrecord function is just a matter of iterating forward over each character looking for the character that is the first character in name. Once found, you simply compare then next nlen character to see if you have found name in buf. You can easily do that with:
char *np = name, /* pointer to name */
*bp = p; /* current pointer in buf */
...
for (i = 0; /* compre name in buf */
i < nlen && *np && *bp && *np == *bp;
i++, np++, bp++) {}
/* validate nlen chars match in buf */
if (np - name == nlen && *(np-1) == *(bp-1)) {
One you have validated you found name in buf, simply copy buf to record insuring your nul-terminate record when done copying buf, e.g.
if (np - name == nlen && *(np-1) == *(bp-1)) {
bp = buf;
for (i = 0; buf[i]; i++) /* copy buf to record */
record[i] = buf[i];
record[i] = buf[i]; /* nul-terminate */
return record; /* return record */
}
Putting it altogether, you could do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
enum { MAXC = 100, MAXL = 1000 }; /* if you need constants, define them */
char *findrecord (FILE *fp, char *name, char *record);
/* main is type 'int', and has arguments -- use them */
int main (int argc, char **argv) {
char name[MAXC] = "",
replace[MAXC] = "",
record[MAXL] = "",
*matched;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : NULL;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* prompt, read, validate name */
printf ("Please input a name in the phonebook: ");
if (scanf ("%99[^\n]%*c", name) != 1) {
fprintf (stderr, "error: invalid input - name.\n");
return 1;
}
/* prompt, read, validate replace */
printf("Please input a replacement name: ");
if (scanf ("%99[^\n]%*c", replace) != 1) {
fprintf (stderr, "error: invalid input - replace.\n");
return 1;
}
/* search name, copy record, return indicates success/failure */
matched = findrecord (fp, name, record);
if (fp != stdin) fclose (fp); /* close file if not stdin */
if (matched) { /* if name matched */
printf ("record : '%s'\n", record);
}
return 0; /* main() returns a value */
}
char *findrecord (FILE *fp, char *name, char *record){
char buf[MAXL] = ""; /* buf for line */
while (fgets (buf, MAXL, fp)) { /* for each line */
char *p = buf;
int blen = 0;
for (; buf[blen]; blen++) {} /* get line length */
if (blen && buf[blen - 1] == '\n') /* check/remove '\n' */
buf[--blen] = 0; /* overwrite with '\0' */
for (; *p && *p != '\n'; p++) /* for each char in line */
if (*p == *name) { /* match start of name? */
char *np = name, /* pointer to name */
*bp = p; /* current pointer in buf */
int i = 0, /* general 'i' var */
nlen = 0; /* name length var */
for (nlen = 0; name[nlen]; nlen++) {} /* name length */
for (i = 0; /* compre name in buf */
i < nlen && *np && *bp && *np == *bp;
i++, np++, bp++) {}
/* validate nlen chars match in buf */
if (np - name == nlen && *(np-1) == *(bp-1)) {
bp = buf;
for (i = 0; buf[i]; i++) /* copy buf to record */
record[i] = buf[i];
record[i] = buf[i]; /* nul-terminate */
return record; /* return record */
}
}
}
return NULL; /* indicate no match in file */
}
Example Use/Output
$ ./bin/findrec dat/bob.csv
Please input a name in the phonebook: Bob
Please input a replacement name: Sam
record : 'Bob, 13, 12345612'
Non-Match Example
$ ./bin/findrec dat/bob.csv
Please input a name in the phonebook: Jerry
Please input a replacement name: Bob
Look things over and let me know if you have further questions.
Is it possible to read lines of text with scanf() - excluding \n and break on special(chosen) character, but include that character
This is my current expression: while(scanf("%49[^:\n]%*c", x)==1)
but this one excludes :.
Is it possible to break reading on : but read that character too?
Ok I am using Johannes-Schaub-litb's code.
char * getline(char cp) {
char * line = malloc(100), * linep = line;
size_t lenmax = 100, len = lenmax;
int c;
if(line == NULL)
return NULL;
for(;;) {
c = fgetc(stdin);
if(c == EOF)
break;
if(--len == 0) {
len = lenmax;
intptr_t diff = line - linep;
char * linen = realloc(linep, lenmax *= 2);
if(linen == NULL) {
free(linep);
return NULL;
}
line = linen + diff;
linep = linen;
}
if((*line++ = c) == cp)
break;
}
*line = '\0';
return linep;
}
Still I use this code ...and it works fine.
The code will be modified a bit more later.
Is it possible to read lines of text with scanf() - excluding \n and break on special(chosen) character, but include that character(?)
Yes. But scanf() is notorious for being used wrong and difficult to use right. Certainly the scanf() approach will work for most user input. Only a rare implementation will completely meet OP's goal without missing corner cases. IMO, it is not worth it.
Alternatively, let us try the direct approach, repeatedly use fgetc(). Some untested code:
char *luvatar_readline(char *destination, size_t size, char special) {
assert(size > 0);
char *p = destitution;
int ch;
while (((ch = fgetc(stdin)) != EOF) && (ch != '\n')) {
if (size > 1) {
size--;
*p++ = ch;
} else {
// Ignore extra input or
// TBD what should be done if no room left
}
if (ch == (unsigned char) special) {
break;
}
}
*p = '\0';
if (ch == EOF) {
// If rare input error
if (ferror(stdin)) {
return NULL;
}
// If nothing read and end-of-file
if ((p == destination) && feof(stdin)) {
return NULL;
}
}
return destination;
}
Sample usage
char buffer[50];
while (luvatar_readline(buffer, sizeof buffer_t, ':')) {
puts(buffer);
}
Corner cases TBD: Unclear what OP wants if special is '\n' or '\0'.
OP's while(scanf("%49[^:\n]%*c", x)==1) has many problems.
Does not cope with input the begins with : or '\n', leaving x unset.
Does not know if the character after the non-:, non-'\n' input was a :, '\n', EOF.
Does not consume extra input past 49.
Uses a fixed spacial character ':', rather than a general one.
I think that you want to do that:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *line = NULL;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, stdin)) != -1) {
if (read > 0 && line[read - 1] == '\n') {
if (read > 1 && line[read - 2] == '\r') {
line[read - 2] = '\0'; // we can remove the carriage return
}
else {
line[read - 1] = '\0'; // we can remove the new line
}
}
char const *delim = ":";
printf("parsing line :\n");
char *token = strtok(line, delim);
while (token != NULL) {
printf("token: %s\n", token);
token = strtok(NULL, delim);
}
}
free(line);
}
I have done this in a little different way. Maybe this can crash on Windows.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *input_str;
/**
* Dynamic memory allocation.
* Might crash on windows.
*/
int status = scanf("%m[^.]", &input_str);
/**
* If the first character is the
* terminating character then scanf scans nothing
* and returns 0.
*/
if (status > 0) {
/**
* Calculate the length of the string.
*/
size_t len = strlen(input_str);
/**
* While allocating memory provide
* two extra cell. One for the character
* you want to include.
* One for the NULL character.
*/
char *new_str = (char*) calloc (len + 2, sizeof(char));
/**
* Check for memory allocation.
*/
if(new_str == NULL) {
printf("Memory Allocation failed\n");
exit(1);
}
/**
* Copy the string.
*/
strcpy(new_str, input_str, len);
/**
* get your desired terminating character
* from buffer
*/
new_str[len++] = getc(stdin);
/**
* Append the NULL character
*/
new_str[len++] = '\0';
/**
* eat other characters
* from buffer.
*/
while(getc(stdin) != '\n');
/**
* Free the memory used in
* dynamic memory allocation
* in scanf. Which is a must
* according to the scanf man page.
*/
free(input_str);
} else {
char new_str[2] = ".\0";
/**
* eat other characters
* from buffer.
*/
while(getc(stdin) != '\n');
}
}
I have used dot as a terminating character.
I am trying to read a text file containing the string "a3rm5t?7!z*&gzt9v" and put all the numeric characters into a character string to later convert into an integer.
I am currently trying to do this by using sscanf on the buffer after reading the file, and then using sprintf to save all characters found using %u in a character string called str.
However, the integer that is returning when I call printf on str is different each time I run the program. What am I doing right and what am I doing wrong?
This code works when the text file contains a string like "23dog" and returns 23 but not when the string is something like 23dog2.
EDIT: I now realize that i should be putting the numeric characters in a character ARRAY rather than just one string.
int main(int argc, const char **argv)
{
int in;
char buffer[128];
char *str;
FILE *input;
in = open(argv[1], O_RDONLY);
read(in, buffer, 128);
unsigned x;
sscanf(buffer, "%u", &x);
sprintf(str,"%u\n", x);
printf("%s\n",str);
close (in);
exit(0);
}
If you simply want to filter out any non-digits from your input, you need not use scanf, sprintf and the like. Simply loop over the buffer and copy the characters that are digits.
The following program only works for a single line of input read from standard input and only if it is less than 512 characters long but it should give you the correct idea.
#include <stdio.h>
#define BUFFER_SIZE 512
int
main()
{
char buffer[BUFFER_SIZE]; /* Here we read into. */
char digits[BUFFER_SIZE]; /* Here we insert the digits. */
char * pos;
size_t i = 0;
/* Read one line of input (max BUFFER_SIZE - 1 characters). */
if (!fgets(buffer, BUFFER_SIZE, stdin))
{
perror("fgets");
return 1;
}
/* Loop over the (NUL terminated) buffer. */
for (pos = buffer; *pos; ++pos)
{
if (*pos >= '0' && *pos <= '9')
{
/* It's a digit: copy it over. */
digits[i++] = *pos;
}
}
digits[i] = '\0'; /* NUL terminate the string. */
printf("%s\n", digits);
return 0;
}
A good approach to any problem like this is to read the entire line into a buffer and then assign a pointer to the buffer. You can then use the pointer to step through the buffer reading each character and acting on it appropriately. The following is one example of this approach. getline is used to read the line from the file (it has the advantage of allocating space for buffer and returning the number of characters read). You then allocate space for the character string based on the size of buffer as returned by getline. Remember, when done, you are responsible for freeing the memory allocated by getline.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, const char **argv)
{
char *buffer = NULL; /* forces getline to allocate required space */
ssize_t read = 0; /* number of characters read by getline */
size_t n = 0; /* limit of characters to read, (0 no limit) */
char *str = NULL; /* string to hold digits read from file */
char *p = NULL; /* ptr to use with buffer (could use buffer) */
int idx = 0; /* index for adding digits to str */
int number = 0; /* int to hold number parsed from file */
FILE *input;
/* validate input */
if (argc < 2) { printf ("Error: insufficient input. Usage: %s filename\n", argv[0]); return 1; }
/* open and validate file */
input = fopen(argv[1], "r");
if (!input) { printf ("Error: failed to open file '%s\n", argv[1]); return 1; }
/* read line from file with getline */
if ((read = getline (&buffer, &n, input)) != -1)
{
str = malloc (sizeof (char) * read); /* allocate memory for str */
p = buffer; /* set pointer to buffer */
while (*p) /* read each char in buffer */
{
if (*p > 0x2f && *p < 0x3a) /* if char is digit 0-9 */
{
str[idx] = *p; /* copy to str at idx */
idx++; /* increment idx */
}
p++; /* increment pointer */
}
str[idx] = 0; /* null-terminate str */
number = atoi (str); /* convert str to int */
printf ("\n string : %s number : %d\n\n", buffer, number);
} else {
printf ("Error: nothing read from file '%s\n", argv[1]);
return 1;
}
if (input) fclose (input); /* close input file stream */
if (buffer) free (buffer); /* free memory allocated by getline */
if (str) free (str); /* free memory allocated to str */
return 0;
}
datafile:
$ cat dat/fwintstr.dat
a3rm5t?7!z*&gzt9v
output:
$ ./bin/prsint dat/fwintstr.dat
string : a3rm5t?7!z*&gzt9v
number : 3579