Reputation: 173
I try to use fscanf() to input parameters from txt file. Here's my code:
fpin = fopen("parameter_file.txt","r");
fscanf(fpin,"%d\n", &code_value);
fscanf(fpin,"%d\n", &origin_x);
fscanf(fpin,"%d\n", &origin_y);
fscanf(fpin,"%d\n", &dpi);
fscanf(fpin,"%d\n", &width);
fscanf(fpin,"%d\n", &height);
And the parameter_file.txt is(for exemple):
333
123
123
800
6
8
I want to add some annotation after each line. For example:
333 //code_value
or 123 //origin_x
My problem is when I add some characters, fscanf() does not work as I desired to read the continues parameters.
I have tried fscanf(fpin,"%[^a-z]%d\n", &code_value);
Another problem is when the input file as follow, how can my code realize?
333 //code_value_1
123 //origin_x_1
123 //origin_y_1
800 //dpi_1
6 //width_1
8 //height_1
12 // code_value_2
10 //origin_x_2
20 //origin_y_2
800 //dpi_2
10 //width_2
10 //height_2
.
.
.
20 // code_value_n
10 //origin_x_n
20 //origin_y_n
800 //dpi_n
30 //width_n
30 //height_n
Upvotes: 3
Views: 9847
Reputation: 70472
If the format is always one number per line, you can use:
fscanf(fpin, "%d%*[^\n]", &var);
The %*[^\n]
will swallow all characters up to the newline character. The *
is the suppression, and the ^\n
means all characters except the newline character is part of the scan set.
However, this will fail on your old style input if a newline immediately follows the number. The more robust method is to read the entire line into a buffer first, and then parse the buffer, with sscanf()
or strtol()
.
char buf[MAX_LINE_LENGTH];
if (fgets(buf, sizeof(buf), fpin) != 0) {
if (sscanf(buf, "%d", &var) == 1) {
/* ... */
} else {
/* ...handle parse error */
}
} else {
/* ...handle file read error */
}
In C++, it can be done similarly using getline()
and an istringstream
.
std::string line;
std::istringstream iss;
if (std::getline(std::cin, line)) {
iss.str(line);
if (iss >> var) {
//...
} else {
//...handle scan error
}
} else {
//...handle read error
}
In a comment, you ask:
How to assign every cin to different parameter?
If each comment describes what the value represents, you can use a map<string, int>
to create an association of names to values. Since the comments do not follow a regular format, you have to get tricky with the parsing.
The C version uses an array to store the associations. You can then later sort it (with qsort()
) and perform binary search on it when you need to do a look up (with bsearch()
).
struct value_item {
char name[MAX_NAME_SIZE];
int value;
};
struct value_item value_map[MAX_VALUE_MAP];
size_t value_map_size = 0;
while (fgets(buf, sizeof(buf), fpin) != 0) {
const char *slash2 = strstr(buf, "//");
if (slash2 && slash2[2] != '\0') {
sscanf(buf, "%d", &value_map[value_map_size].value);
sscanf(slash2+2, "%s", value_map[value_map_size].name);
++value_map_size;
} else {
/* ...handle parse error */
}
}
The C++ version would look similar. C++ provides map
, which you can use to easily create the association.
std::map<std::string, int> value_map;
while (std::getline(std::cin, line)) {
std::string::size_type n = line.find("//");
if (n != std::string::npos && line.size() > (n+2)) {
int value;
std::string name;
iss.str(line);
iss >> value;
iss.str(line.substr(n+2));
iss >> name;
value_map[name] = value;
} else {
//...handle parse error
}
}
I am writing this off the top of my head, so there may be errors, and nicer ways to do it. But, this should give you the general idea.
Upvotes: 5