scanf を簡略化したい
-
- 記事: 17
- 登録日時: 5年前
scanf を簡略化したい
~標準入力 を簡略化(?)する関数の作成について~
最近になってiostreamよりもcstdioでの標準入出力が速いことを知りました。
そこで以下のような関数の作成をしようとしています。
関数inの<>内のクラスによって挙動が変わり、in<int>であれば関数の中身が in<char>であれば関数の中身が in<string>であれば関数の中身が となるようにしたいのです。
すると、以下のように変数の宣言と同時に入力が可能となるはずです。 自分で調べていく中でテンプレートの特殊化を使えば作れると思うのですが、そもそもテンプレートの特殊化をあまり使ったことがなく、また、テンプレートの特殊化について自分が理解できるような文献を見つけることが出来ませんでした。
テンプレートの特殊化を使った場合この関数の実装はどうなるのか、またそれ以外に良い方法があれば教えていただきたいです。
最近になってiostreamよりもcstdioでの標準入出力が速いことを知りました。
そこで以下のような関数の作成をしようとしています。
関数inの<>内のクラスによって挙動が変わり、in<int>であれば関数の中身が in<char>であれば関数の中身が in<string>であれば関数の中身が となるようにしたいのです。
すると、以下のように変数の宣言と同時に入力が可能となるはずです。 自分で調べていく中でテンプレートの特殊化を使えば作れると思うのですが、そもそもテンプレートの特殊化をあまり使ったことがなく、また、テンプレートの特殊化について自分が理解できるような文献を見つけることが出来ませんでした。
テンプレートの特殊化を使った場合この関数の実装はどうなるのか、またそれ以外に良い方法があれば教えていただきたいです。
Re: scanf を簡略化したい
「C++ テンプレート 特殊化」でググって最初に出てきた
テンプレートの特殊化 | Programming Place Plus C++編【言語解説】 第23章
を参考に、こんな感じでしょうか。
テンプレートの特殊化 | Programming Place Plus C++編【言語解説】 第23章
を参考に、こんな感じでしょうか。
#include <iostream>
#include <cstdio>
#include <string>
using std::cin;
using std::string;
template <typename T>
T in() {
T temp; cin >> temp; return temp;
}
template <>
int in() {
int temp; scanf("%d", &temp); return temp;
}
template <>
char in() {
char temp; scanf("%c", &temp); return temp;
}
int main(void) {
int a = in<int>(); string b = in<string>();
printf("%d %s\n", a, b.c_str());
return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
-
- 記事: 17
- 登録日時: 5年前
Re: scanf を簡略化したい
回答ありがとうございます。テンプレートの特殊化に関して使い方が分かった気がします。みけCAT さんが書きました: ↑5年前「C++ テンプレート 特殊化」でググって最初に出てきた
テンプレートの特殊化 | Programming Place Plus C++編【言語解説】 第23章
を参考に、こんな感じでしょうか。 …
書いていただいたコードの入力書式のパターンを増やそうとすると
using uint = unsigned int;
using lint = long;
using ushort = unsigned short;
using ulong = unsigned long;
template<typename var>
var in() { var temp; cin >> temp; return temp; }
template<>
char in() { char temp; scanf("%c", &temp); return temp; }
template<>
int in() { int temp; scanf("%d", &temp); return temp; }
template<>
uint in() { uint temp; scanf("%u", &temp); return temp; }
template<>
float in() { float temp; scanf("%f", &temp); return temp; }
template<>
short in() { short temp; scanf("%hd", &temp); return temp; }
template<>
lint in() { lint temp; scanf("%ld", &temp); return temp; }
template<>
ushort in() { ushort temp; scanf("%hu", &temp); return temp; }
template<>
ulong in() { ulong temp; scanf("%lu", &temp); return temp; }
template<>
double in() { double temp; scanf("%lf", &temp); return temp; }
var in() の中で if(typeid(var)==typeid(int)){... のようにif文を複数使うことを検討したのですが、短くなるどころか長くなってしまいました。
Re: scanf を簡略化したい
マクロを使えば短くはなりますが、いかがでしょうか?
#include <iostream>
#include <string>
using uint = unsigned int;
using ushort = unsigned short;
using ulong = unsigned long;
template<typename T> T in() { T v; std::cin >> v; return v; }
#define IN(T, F) template<> T in() { T v; scanf(F, &v); return v; }
IN(char, " %c") IN(int, "%d") IN(uint, "%u")
IN(short, "%hd") IN(ushort, "%hu") IN(long, "%ld") IN(ulong, "%lu")
IN(float, "%f") IN(double, "%lf")
int main()
{
char c = in<char>(); std::cout << "c = " << c << "\n";
int i = in<int>(); std::cout << "i = " << i << "\n";
uint u = in<uint>(); std::cout << "u = " << u << "\n";
short h = in<short>(); std::cout << "h = " << h << "\n";
ushort H = in<ushort>(); std::cout << "H = " << H << "\n";
long l = in<long>(); std::cout << "l = " << l << "\n";
ulong L = in<ulong>(); std::cout << "L = " << L << "\n";
float f = in<float>(); std::cout << "f = " << f << "\n";
double d = in<double>(); std::cout << "d = " << d << "\n";
std::string s = in<std::string>(); std::cout << "s = " << s << "l\n";
}
A
-1234567890
2147483648
-32768
65535
-2147483648
4294967295
2.2360679
3.1415926535897932
string
-
- 記事: 17
- 登録日時: 5年前
Re: scanf を簡略化したい
なるほど。マクロを使うという手があったんですね。
とても助かりました。ありがとうございました。
とても助かりました。ありがとうございました。
Re: scanf を簡略化したい
呼び出し時にいちいちテンプレート型引数を書かなきゃいけないのがめんどくさそう.
【特定の型に関してはscanf()を使いたいが,他の型(? 少なくともstring)に関してはiostream 使う】みたいな話であれば,
↓のような感じにすればどうか.
【特定の型に関してはscanf()を使いたいが,他の型(? 少なくともstring)に関してはiostream 使う】みたいな話であれば,
↓のような感じにすればどうか.
-
- 記事: 17
- 登録日時: 5年前
Re: scanf を簡略化したい
このような書き方をするとcinと似た形で使えるんですね。回答ありがとうございます。
本題とは少しずれるのですが、cinはiostreamをインクルードしてstd::cin>>s;のように使えますが、
今回提示いただいたコードだと一度IN inと宣言が入ってからin>>s;となっていると思います。
何かコードに工夫をし、IN inの宣言なしにin>>s;と書くことはできるのでしょうか。
Re: scanf を簡略化したい
IN in; の宣言をなくすのなら、in >> s; を IN() >> s; と書けばm3908714035 さんが書きました: ↑5年前本題とは少しずれるのですが、cinはiostreamをインクルードしてstd::cin>>s;のように使えますが、
今回提示いただいたコードだと一度IN inと宣言が入ってからin>>s;となっていると思います。
何かコードに工夫をし、IN inの宣言なしにin>>s;と書くことはできるのでしょうか。
よいのではありませんか?
宣言でオブジェクトを確保するのではなく、コンストラクタの
呼び出しによる一時オブジェクトを使えばよい、ということです。
クラス名を in にし、マクロ名を IN にすると、
#include <iostream>
#include <string>
using uint = unsigned int;
using ulong = unsigned long;
using ushort = unsigned short;
#define IN(T, F) in& operator>>(T& v) { scanf(F, &v); return *this; }
struct in {
template<typename T> in& operator>>(T& v) { std::cin >> v; return *this; }
IN(char, " %c") IN(int, "%d") IN(uint, "%u")
IN(short, "%hd") IN(ushort, "%hu") IN(long, "%ld") IN(ulong, "%lu")
IN(float, "%f") IN(double, "%lf")
};
int main()
{
char c; int i; uint u; short h; ushort H; long l; ulong L;
float f; double d; std::string s;
in() >> c >> i >> u >> h >> H >> l >> L >> f >> d >> s;
std::cout << c << "\n" << i << "\n" << u << "\n" << h << "\n" << H << "\n"
<< l << "\n" << L << "\n" << f << "\n" << d << "\n" << s << "\n";
}
実行時に書式を解釈しないといけないので遅くなるような気がします。
まあ、実装に依存するんでしょうね。
fgetc や fgets は cin >> c や getline(cin, s) より速そうですが、
getline は、string s なので、サイズの制限がありませんよね。
動的にメモリを確保するから遅いのではありませんか?
また、このやり方だと EOF やエラーの検出はどうするのでしょうか?
-
- 記事: 17
- 登録日時: 5年前
Re: scanf を簡略化したい
下記リンク先からの引用となります。かずま さんが書きました: ↑5年前ところで、本当に scanf は速いんでしょうか?
実行時に書式を解釈しないといけないので遅くなるような気がします。
まあ、実装に依存するんでしょうね。
fgetc や fgets は cin >> c や getline(cin, s) より速そうですが、
getline は、string s なので、サイズの制限がありませんよね。
動的にメモリを確保するから遅いのではありませんか?
C/C++ におけるデータ入力の速度
1000 万行のテキストファイルを C/C++ で作成したプログラムで読み込むとき,どのくらいの時間がかかるかを調べた結果です。
どうやらfgets>getline(std::ios_base::sync_with_stdio(false)有のもの)>>fgetcとなるようです。関数の中身までは把握しきれていないので、どうしてこのような結果になるのかは分かりませんが…。※ 1. nosync: std::ios_base::sync_with_stdio(false)
※ 2. _IONBF: std::setvbuf(stdin, NULL, _IONBF, 0)
※ 3. _IOLBF: std::setvbuf(stdin, io_buf, _IOLBF, 1048576)
※ 4. _IOFBF: std::setvbuf(stdin, io_buf, _IOFBF, 1048576)
※ 5. real, user, sys: time -f 'real %E, user %U, sys %S'
また 競プロ初心者が書く「標準入出力からはじめる競プロ入門」 では、10^6個のランダムな符号付き32ビット整数のデータの入力がiostream - 4.96sに対し、cstdio - 0.42sという結果もありました。
scanfは基礎を学ぶ際によく用いていたということもあり、今回はscanfでの入力を考えました。もし、私が勘違いをしていて「やっぱりscanf遅いで!」みたいなことがあれば教えていただけると幸いです。
この件に関しては、今回のコードは私の中で競技プログラミングにおけるコードの高速化を想定していたので、考えていませんでした。以前、cinでのエラーやEOFについては考えたことがあり、かずま さんが書きました: ↑5年前また、このやり方だと EOF やエラーの検出はどうするのでしょうか?
上のようなコード(一部抜粋)を書いた記憶があります。しかし、scanfにおけるエラーやEOFに関しては全くの無知ですので、質問を返すようで申し訳ないのですが、逆に教えていただきたく思います。
Re: scanf を簡略化したい
単純にオブジェクトを1個作ってその名前を公開しておけば良いのではないかと.m3908714035 さんが書きました: ↑5年前本題とは少しずれるのですが、cinはiostreamをインクルードしてstd::cin>>s;のように使えますが、
今回提示いただいたコードだと一度IN inと宣言が入ってからin>>s;となっていると思います。
何かコードに工夫をし、IN inの宣言なしにin>>s;と書くことはできるのでしょうか。
(cinだって,単に cinという名前のグローバルなオブジェクトが存在しているだけでしょうから)
-
- 記事: 17
- 登録日時: 5年前
Re: scanf を簡略化したい
scanf は遅いはずだと思って測ってみました。
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
実行結果
使用したファイルは 10000000バイト以上あります。
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
#include <iostream>
#include <fstream>
#include <cstdio>
#include <ctime>
using namespace std;
const int N = 10000000;
char buf[N];
void read_char()
{
clock_t t0, t1;
FILE *fp;
fp = fopen("/usr/bin/nodejs", "r");
if (!fp) return;
t0 = clock();
for (int i = 0; i < N; i++)
buf[i] = fgetc(fp);
t1 = clock();
fclose(fp);
printf("%f sec: fgetc\n", (t1 - t0) / double(CLOCKS_PER_SEC));
ifstream ifs("/usr/bin/nodejs");
if (!ifs) return;
t0 = clock();
for (int i = 0; i < N; i++)
ifs >> buf[i];
t1 = clock();
ifs.close();
printf("%f sec: ifs >>\n", (t1 - t0) / double(CLOCKS_PER_SEC));
fp = fopen("/usr/bin/nodejs", "r");
if (!fp) return;
t0 = clock();
for (int i = 0; i < N; i++)
fscanf(fp, "%c", &buf[i]);
t1 = clock();
fclose(fp);
printf("%f sec: fscnaf\n", (t1 - t0) / double(CLOCKS_PER_SEC));
}
int main()
{
ios_base::sync_with_stdio(false);
for (int i = 0; i < 3; i++) {
read_char();
puts("---");
}
}
0.172367 sec: fgetc
0.293313 sec: ifs >>
0.599683 sec: fscnaf
---
0.165610 sec: fgetc
0.294515 sec: ifs >>
0.598882 sec: fscnaf
---
0.167828 sec: fgetc
0.294134 sec: ifs >>
0.601244 sec: fscnaf
---
Re: scanf を簡略化したい
最初 getchar, cin >>, scanf でやっていたのを
fgetc, ifs >>, fscanf に変更したので、
sync_with_stdio(false) は無意味になりました。
また、cin >> noskipws; または ifs >> noskipws; を
実行しておかないと、スペースや改行などの空白が読み飛ばされますから、
getchar, scanf("%c" よりもファイルを多く読むことになります。
忘れていました。
fgetc, ifs >>, fscanf に変更したので、
sync_with_stdio(false) は無意味になりました。
また、cin >> noskipws; または ifs >> noskipws; を
実行しておかないと、スペースや改行などの空白が読み飛ばされますから、
getchar, scanf("%c" よりもファイルを多く読むことになります。
忘れていました。