Scanf on an Istream Object

scanf on an istream object

Based on @tmyklebu's comment, I implemented streamScanf which wraps istream as FILE* via fopencookie: https://github.com/likan999/codejam/blob/master/Common/StreamScanf.cpp

overriding `istream operator` vs using `sscanf`

Since you are reading from a file, the performance is going to be I/O-bound. Almost no matter what you do in memory, the effect on the overall performance is not going to be detectable.

I would prefer the operator>> route, because this would let me use the input iterator idiom of C++:

std::istream_iterator<Person> eos;
std::istream_iterator<Person> iit(inputFile);
std::copy(iit, eos, std::back_inserter(person_vector));

or even

std::vector<Person>   person_vector(
std::istream_iterator<Person>(inputFile)
, std::istream_iterator<Person>()
);

What is the cin analougus of scanf formatted input?

Use the >> operator to read from cin.

  int number1, number2;
std::string text;
char plus, equals;
std::cin >> number1 >> plus >> number2 >> equals >> text;
if (!std::cin.fail() && plus == '+' && equals == '=' && !text.empty())
std::cout << "matched";

It's not as good as scanf because you'd have to verify any literals that were in the scanf string yourself. Doing it with streams will almost certainly be a lot more lines of code than scanf.

I would use scanf.

Skipping expected characters like scanf() with cin

You could create your own stream manipulator. It is fairly easy.

#include <ios>
#include <iostream>
using namespace std;

// skips the number of characters equal to the length of given text
// does not check whether the skipped characters are the same as it
struct skip
{
const char * text;
skip(const char * text) : text(text) {}
};

std::istream & operator >> (std::istream & stream, const skip & x)
{
ios_base::fmtflags f = stream.flags();
stream >> noskipws;

char c;
const char * text = x.text;
while (stream && *text++)
stream >> c;

stream.flags(f);
return stream;
}

int main()
{
int a, b;
cin >> a >> skip(" # ") >> b;
cout << a << ", " << b << endl;
return 0;
}

In C++, how to construct an object referencing to different istream object depending on runtime condition?

You might simply create a function for // work on reader:

void do_work(LineReader& reader)
{
// work on reader
}

int main(int argc, char *argv[]) {
if (argc > 1) { //read from file
std::ifstream in_f(argv[1]);
if (in_f) {
LineReader reader(in_f);
do_work(reader);
}
}
else { //read from console (standard input)
LineReader reader(std::cin);
do_work(reader);
}
}

or reorganize your code to got reference to std::istream:

std::istream& get_istream(std::ifstream& in_f, int argc, char* argv[])
{
if (argc > 1) { //read from file
in_f.open(argv[1]);
}
if (in_f) {
return in_f;
} else {
return std::cin;
}
}

int main(int argc, char *argv[]) {
std::ifstream in_f;
LineReader reader(get_istream(in_f, argc, argv));

// work on reader
}

Does cout/cin internally call printf() / scanf() like `new` calls malloc?

The C++ standard does not specify how standard library facilities such as std::cin and std::cout are implemented, only how they should behave. Whether the C++ I/O functions call their C counterparts is up to the implementation.

As an example of how the C++ I/O streams can be implemented, we can look at the source code of libstdc++, which is GCC's standard library implementation. The std::basic_istream& operator>>(int&) function which is the one called when you use std::cin >> x to read an int calls some functions which call other functions and it eventually reaches this _M_extract_int function that actually parses the integer. Therefore, libstdc++ does not implement the stream extraction operator for ints using C I/O functions. Still, remember that this is only one example and other standard library implementations may be different.

Using scanf() in C++ programs is faster than using cin?

Here's a quick test of a simple case: a program to read a list of numbers from standard input and XOR all of the numbers.

iostream version:

#include <iostream>

int main(int argc, char **argv) {

int parity = 0;
int x;

while (std::cin >> x)
parity ^= x;
std::cout << parity << std::endl;

return 0;
}

scanf version:

#include <stdio.h>

int main(int argc, char **argv) {

int parity = 0;
int x;

while (1 == scanf("%d", &x))
parity ^= x;
printf("%d\n", parity);

return 0;
}

Results

Using a third program, I generated a text file containing 33,280,276 random numbers. The execution times are:

iostream version:  24.3 seconds
scanf version: 6.4 seconds

Changing the compiler's optimization settings didn't seem to change the results much at all.

Thus: there really is a speed difference.


EDIT: User clyfish points out below that the speed difference is largely due to the iostream I/O functions maintaining synchronization with the C I/O functions. We can turn this off with a call to std::ios::sync_with_stdio(false);:

#include <iostream>

int main(int argc, char **argv) {

int parity = 0;
int x;

std::ios::sync_with_stdio(false);

while (std::cin >> x)
parity ^= x;
std::cout << parity << std::endl;

return 0;
}

New results:

iostream version:                       21.9 seconds
scanf version: 6.8 seconds
iostream with sync_with_stdio(false): 5.5 seconds

C++ iostream wins! It turns out that this internal syncing / flushing is what normally slows down iostream i/o. If we're not mixing stdio and iostream, we can turn it off, and then iostream is fastest.

The code: https://gist.github.com/3845568

Linux, scanf in loop, while stdin already loaded

When you call

scanf(" %s", s);

for the first time, it sets s to lol, and returns 1. When you call it for the second time, there is no further input, so scanf returns zero without setting your variable. However, your code ignores the return value of scanf, and prints s anyway.

Adding a check and a break should fix this problem:

if (scanf(" %254s", s) != 1) break;

Note the size limiter in the format string, it will prevent scanf from causing buffer overruns.



Related Topics



Leave a reply



Submit