I'm trying to search a text file using C for a string of characters that will always be in a specific place. Specifically I'm looking for three sequential dashes. When it finds the three dashes it should return the line on which it was found. It should then continue to the next line and continue searching for the three sequential dashes until it reaches the end of the file. Each time is should print the line number.
This is what I have so far:
int main() {
FILE *f;
char inName[80];
printf("Read text from: ");
scanf(" %79[^\n]s\n", inName);
f = fopen(inName, "r");
if (f == 0) printf("ERROR: Cannot open file %s for reading.\n", inName);
int lineNumber = 0;
for(;;) {
char line[127];
fgets(line, 127, f);
if (!feof(f)) {
lineNumber++;
} else {
break;
}
double lf;
int d, d1;
char s[30];
char s1[4];
sscanf(line, " %d, %s, %s, %s, %s, %d, %d, %lf",
&d, &s, &s, &s, &s, &d, &s1, &lf);
if (s1 == "---") {
printf("%d\n", lineNumber); // what line
}
}
fclose(f);
return(0);
}
This code runs but does not print anything. Could anyone show how to get this done? Thanks :)
if (s1 == "---")
You're comparing strings the wrong way, you should use strcmp()
follow this example
http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1057537653&id=1043284385
This is not how to compare strings in C:
if (s1 == "---")
as is comparing the address of s1 with the address of the string literal "---".
Use strcmp():
if (strcmp(s1, "---") == 0)
{
Always check the return value of sscanf() to ensure the variables have actually been assigned a value before attempting to use them. The , comma character is not treated as delimiter when processed with "%s" format specifier, only whitespace is used as delimiter. To prevent the consumption of the , use a scan set, similar to how you have done earlier in the program (note corrections to sscanf() arguments):
if (sscanf(line,
" %d, %29[^,], %29[^,], %29[^,], %29[^,], %d, %3[^,], %lf",
&d, s, s, s, s, &d, s1, &lf) == 8)
{
}
You cannot compare char[] with ==. Use strcmp instead.
if( strcmp(s1, "---") == 0 )
Related
I'm trying to read formatted data with sscanf in this format:
"%d,%d,%d"
for example,
sscanf(buffer, "%d,%d,%d", n1, n2, n3)
and it is critic that there is no white space between every number, because the input comes like this, for example:
109,304,249
194,204,482
etc.
But when I give sscanf an input like:
13, 56, 89
145, 646, 75
it still reads it even though it is not in the specifed format, and should be invalid
Is there is a way to write something such that sscanf will work as I expect it to work?
scanf ignores leading whitespace for most conversions, but what you want is the opposite of ignoring whitespace.
You cannot tell scanf to error out on a whitespace, but you can detect how many whitespace characters were consumed. For example:
#include <stdio.h>
int main()
{
int first, second, before, after;
int nread;
nread = scanf("%d,%n %n%d", &first, &before, &after, &second);
if (nread != 2) {
printf ("Error in the input stream, 2 items expected, %d matched\n", nread);
}
else if (before != after) {
printf ("Error in the input stream, %d whitespace characters detected\n",
after-before);
}
else {
printf ("Got numbers %d %d\n", first, second);
}
}
This detects whitespace before the second input.
Having said that, erroring out on whitespace is probably not a good idea.
The %d conversion format ignores leading white space as well as an optional + sign. If you want a stricter validation, you can:
use an extra validation phase before or after converting the values.
use a hand coded conversion that rejects malformed input.
Here are some propositions:
#include <stdio.h>
#include <string.h>
int main() {
char input[100];
char verif[100];
int a, b, c;
if (fgets(input, sizeof input, stdin)) {
if (sscanf(input, "%d,%d,%d", &a, &b, &c) == 3) {
snprintf(verif, sizeof verif, "%d,%d,%d\n", a, b, c);
if (strcmp(input, verif) == 0) {
printf("input is valid, a=%d, b=%d, c=%d\n", a, b, c);
return 0;
} else {
printf("input contains extra characters\n");
return 1;
}
} else {
printf("input does not match the pattern\n");
return 1;
}
} else {
printf("no input\n");
return 1;
}
}
You could just check if the input string contains any white space:
if (input[strcspn(input, " \t")] != '\0') {
printf("input string contains spaces or TABs\n");
}
But negative values, redundant + signs and leading zeroes will pass the test (eg: "-1,+1,+0001").
If all values must be positive, you could use scanf() to perform a poor man's pattern matching:
if (fgets(input, sizeof input, stdin)) {
char c1, c2;
if (sscanf(input, "%*[0-9],%*[0-9],%*[0-9]%c%c", &c1, &c2) == 1 && c1 == '\n') {
/* input line contains exactly 3 numbers separated by a `,` */
} else {
printf("invalid format\n");
}
}
Note however these remarks:
redundant leading zeroes would still be accepted by this validation phase (eg: "00,01,02").
overlong numbers will technically cause undefined behavior in all of the above methods (eg: "0,0,999999999999999999999999999999"), But the first approach will detect this problem if the conversion just truncates or maximises the converted value.
an overlong input line (longer than 98 bytes plus a newline) will be truncated by fgets(), leaving the rest of the input for the next read operation. This may be a problem too.
The solution to your problem depends on how strict and concise the validation must be.
One idea to validate the input is to use the regular expression to match three integers delimited by commas without including other characters.
#include <stdio.h>
#include <regex.h>
#include <string.h>
int main()
{
regex_t preg; // regex pattern
int ret;
int n1, n2, n3;
char *p; // pointer to a character th the string
char buffer[BUFSIZ]; // input buffer
// compile regex to match three integers delimited by commas
ret = regcomp(&preg, "^([+-]?[[:digit:]]+,){2}[+-]?[[:digit:]]+$", REG_EXTENDED);
if (ret) {
fprintf(stderr, "Compile error of regex\n");
exit(1);
}
while (fgets(buffer, BUFSIZ, stdin)) {
if ((p = rindex(buffer, '\n'))) *p = 0; // remove trailing newline
ret = regexec(&preg, buffer, 0, NULL, 0);
if (!ret) { // input matches the regex pattern
sscanf(buffer, "%d,%d,%d", &n1, &n2, &n3);
printf("=> %d,%d,%d\n", n1, n2, n3);
} else {
printf("Error in the input: %s\n", buffer);
}
}
return 0;
}
where ^([+-]?[[:digit:]]+,){2}[+-]?[[:digit:]]+$ is the regex:
^ anchors the start of the input string.
[+-]? matches zero or one leading plus or minus sign.
[[:digit:]]+, matches the sequence of digits followed by a comma.
The parentheses ( pattern ) makes a group of the pattern.
The quantifier {2} specifies the number of repetition of the previous atom (including group).
$ anchors the end of the input string.
I have to read a file that has defined pattern: it will allways have a "Letter (single char) Number Number Number String"
For example:
A 20 22 2340 HELLO WORLD
So i'm trying to put each thing in a separeted item of my list.
I know how to get the first char and the 3 numbers and put it into a new variable, but i don't know how to get only the final string. If i use fgets, it will get all the line and, if i use fscanf, it will get the frist "word", but it won't get anything after the space. How can i read only the string and store it into a new variable?
int a, b, c, x, k = 0;
FILE *file;
file = fopen("Test.txt", "r");
while (fgets(buffer, 100, file) != NULL){
sscanf(buffer, "%c %i %i %i ", &a, &b, &c, &x);
Events.Key = k;
Events.Event = a;
Events.Day = b;
Events.Month = c;
Events.Year = x;
Insertion(Events, Data); //call my function to insert in the list
k++;
}
In this code, i can get the value from (my previous example) A 20 22 2340, but, if i put another %s and a string in the sscanf, it will get only "HELLO" and, if i use fgets, it will get the whole line. How can i get the whole string, including the spaces?
Thanks
You can use the string set specifier %[] which stops scanning where you tell it. Note that fgets() retains the trailing newline so it is convenient to stop there, and filter it out.
char name[50];
if(sscanf(buffer, "%c %i %i %i %49[^\n]", &a, &b, &c, &x, name) != 5)
{
// handle error
}
printf("%s\n", name);
Note the type error for %c as commented.
Got it, guys, i used [^\n], i didn't know about that function, thanks!
The user will enter the configuration of the bus, like this
2
OO|XO
XX|XX
where
the number in the first line (let n) tells the number of rows, and
the following n lines show a pair of two seats
with a walkway in between denoted by |, a pipe.
A seat can either be occupied or empty. An empty seat is denoted by an O; an occupied one by an X.
char str1[3], str2[3];
I'm treating each pair of seats as a string,
char pipe;
and the pipe as a character.
I tried doing this:
scanf("%s %*c %s", str1, pipe, str2);
and this:
scanf("%s", str1);
pipe = getchar();
scanf("%s", str2);
but still no luck.
The question is: How to enter two/more characters/strings in one go without separating them with any kind of space or break?
scanf lets you specify any separator that you wish to use, including pipe. Since the pipe is always there, you don't need to scan it into your program, instead instructing scanf to skip it:
char a[3], b[3];
scanf("%2s|%2s", a, b);
printf("'%s' '%s'", a, b);
Demo.
First, read the property of %s conversion specifier. The input is delimited by a whitespace, not by a pipe |, at least not automatically.
The easiest workaround would be, to use a maximum field width, something like
scanf("%2s%*c%2s", str1, str2); // the * in %*<CS> indicates assignment suppression,
// you don't need a corresponding argument at all, and
// a wrong one will cause trouble
here, you don't need to modify the code if you wish to chose a different delimiter, it'll consider anything as a delimiter. If you want to enforce the use of |, you can write
scanf("%2s|%2s", str1, str2);
NOTE - never forget to check the return value of scanf() to ensure success.
There are 2 different ways to address your problem:
If the string length is fixed, you can just specify the maximum number of characters to read for %s and use this code:
char str1[3], str2[3];
if (scanf("%2s|%2s", str1, str2) == 2) {
/* read 2 characters into str1, a pipe and 1 or 2 characters into str2 */
}
If the string length is variable, such as on planes with different numbers of seats in some rows, you can use a scanset %[OX] and also specify the number of characters to read to prevent potential buffer overflow on unexpected input. Here is an example:
char str1[5], str2[5]; // handle up to 4 seats on each side */
if (scanf("%4[OX]|%4[OX]", str1, str2) == 2) {
/* read a group of 1 to 4 `X` or `O` into str1 and str2, separated by | */
}
You can add another conversion to further verify that the line has the expected format. Here is an example:
#include <stdio.h>
int main(void) {
char buf[128];
int i, n, c;
char left[3], right[3];
if (fgets(buf, sizeof buf, stdin) == NULL) {
fprintf(stderr, "invalid format, empty file\n");
return 1;
}
if (sscanf(buf, "%d %c", &n, &c) != 1 || n < 0) {
fprintf(stderr, "invalid format, expected positive number: %s\n", buf);
return 1;
}
for (i = 0; i < n; i++) {
if (fgets(buf, sizeof buf, stdin) == NULL) {
fprintf(stderr, "missing %d lines\n", n - i);
return 1;
}
if (sscanf(buf, "%2[XO]|%2[XO] %c", left, right, &c) != 2) {
fprintf(stderr, "invalid format: %s\n", buf);
return 1;
} else {
printf("row %d: %s | %s\n", i + 1 + (i >= 12), left, right);
}
}
return 0;
}
I'm attempting to finish a portion of a program but I can't seem to make sense of how to complete a simple problem. In order for the user to get to the next portion of the program they need to enter a value as a char, decimal, and hex. If they don't the control switches to another function.
I was hoping it would be as easy as
if(input != "%c %d %x") etc.
from what I'm reading it seems to be much more confusing than that and I can't seem to find an answer although I'm sure I overlooked an obvious one.
Edit:
int main() {
char input[512];
int n;
user_id = (char *)malloc(100);
printf("Type your user id\n");
my_fgets(user_id, 100, stdin);
printf("Input for phase 1\n");
my_fgets(input, 512, stdin);
phase_1(input);
n = hash2(user_id);
printf("Your number is %d\n", n);
}
void phase_2(char *input, int n)
{
n = n + 100;
if(input != "%c\n")
explode_bomb();
}
You need to write an input function, which reads input into a string, then use isdigit() ischar() isxchar() to scan the string to find out what type the input is.
I'm assuming you expect input to be something like this: C 67 43. The character C along with its value in decimal and hex. You need to write a function to parse this string and determine if it's correct.
First, the parsing. You're on the right track. What you need is sscanf.
char char_char;
unsigned int char_hex;
unsigned int char_int;
if( sscanf(input, "%c %u %x", &char_char, &char_int, &char_hex) < 3 ) {
fprintf(stderr, "%s doesn't look like what I want\n", input);
return false;
}
I chose unsigned int because %x maps to an unsigned integer and char also can safely be compared with an unsigned integer.
Then all you need to do is check if the three values are the same. sscanf has already converted the hex into an unsigned integer, and char can safely be compared to an unsigned integer.
if( char_int != char_hex ) {
fprintf(stderr, "%u and %u don't match\n", char_int, char_hex);
return false;
}
if( char_int != char_char ) {
fprintf(stderr, "%u and %c don't match\n", char_int, char_char);
return false;
}
return true;
Put it all together and wrap a function around it.
#include <stdbool.h>
#include <stdio.h>
bool check_input(const char *input) {
char char_char;
unsigned int char_int;
unsigned int char_hex;
if( sscanf(input, "%c %d %x", &char_char, &char_int, &char_hex) < 3 ) {
fprintf(stderr, "%s doesn't look like what I want\n", input);
return false;
}
if( char_int != char_hex ) {
fprintf(stderr, "%u and %u don't match\n", char_int, char_hex);
return false;
}
if( char_int != char_char ) {
fprintf(stderr, "%u and %c don't match\n", char_int, char_char);
return false;
}
return true;
}
A better function won't print to stderr, but would instead set the error message via a passed in char *.
In order to test if a string complies with a given format, use "*" and "%n".
The * still scans for text that matches the specifier, but suppresses saving and also does not add to the return value.
"%n" directs *scanf() to save the number of char scanned so far.
By testing if n > 0 and if input[n] is the end of the string, code can simple detect a matching format.
int n = 0;
sscanf(input, "%*c %*d %*x %n", &n);
if (n > 0 && input[n] == '\0') {
puts("Success");
}
I want the user to enter a MAC address in the format : aa:bb:cc:dd:ee:ff and then I take this in my code as argv[1] for example.
Now how do I store this in an array in hex format?
What I need is the array to look like this:
char temp[6] = { 0xaa,0xbb,0xcc,0xdd,0xee,0xff }
Any ideas?
An array is not stored in hex format; hex formatting only applies to reading and printing. Internally, all numbers are stored in binary.
To read hex digits, you can use scanf, and to print hex digits, you can use printf. To let scanf know that you want it to read hex digits, use the format specifier %x. If you want to read into a char instead of an int, use the modifierhh`. Here's an example of how you could use it for your case:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
unsigned char addr[6];
char dummy;
if (argc != 2) {
fprintf(stderr, "Wrong number of arguments\nUsage: %s ADDRESS\n",
argv[0]);
exit(1);
}
int res = sscanf(argv[1], "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx%c",
&addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5],
&dummy);
if (res == EOF) {
fprintf(stderr, "Reached end of input without matching\n");
exit(1);
} else if (res < 6) {
fprintf(stderr, "Got fewer hex digits than expected\n");
exit(1);
} else if (res > 6) {
fprintf(stderr, "Got extra characters after input\n");
exit(1);
}
printf("Got: %02hhx, %02hhx, %02hhx, %02hhx, %02hhx, %02hhx\n",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
return 0;
}
Note that I read in a dummy character after the input, to check for input that contains junk at the end. This may or may not be necessary for your use case.
int a, b, c, d, e, f;
sscanf(argv[1], "%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
Use ints to get the values, then you can place them in char array, since they have byte values.
sscanf(str, "%x:%x:%x:%x:%x:%x", &temp[0], &temp[1], &temp[2], &temp[3], &temp[4], &temp[5]);