c ++ file io&セパレーターによる分割
質問
次のようなデータがリストされたファイルがあります:
0, 2, 10
10, 8, 10
10, 10, 10
10, 16, 10
15, 10, 16
17, 10, 16
ファイルを入力して3つの配列に分割できるようにしたいのですが、その過程で余分なスペースをすべて削除し、各要素を整数に変換します。
何らかの理由で、C ++でこれを行う簡単な方法が見つかりません。私が経験した唯一の成功は、各行を配列に入力し、すべてのスペースを正規表現し、それを分割することです。このプロセス全体で20〜30行のコードが必要になり、別のセパレーター(スペースなど)を変更するのに苦労しました。
これは、C ++で使用したいものと同等のPythonです。
f = open('input_hard.dat')
lines = f.readlines()
f.close()
#declarations
inint, inbase, outbase = [], [], []
#input parsing
for line in lines:
bits = string.split(line, ',')
inint.append(int(bits[0].strip()))
inbase.append(int(bits[1].strip()))
outbase.append(int(bits[2].strip()))
Pythonでこれを行うことの使いやすさが、そもそも私がPythonに移行した理由の1つです。ただし、今はC ++でこれを行う必要があり、andい20〜30行のコードを使用する必要はありません。
ご協力ありがとうございます!
解決
fscanfには実際に問題はありません。おそらく、この場合の最速の解決策です。そして、それはPythonコードと同じくらい短くて読みやすいです:
FILE *fp = fopen("file.dat", "r");
int x, y, z;
std::vector<int> vx, vy, vz;
while (fscanf(fp, "%d, %d, %d", &x, &y, &z) == 3) {
vx.push_back(x);
vy.push_back(y);
vz.push_back(z);
}
fclose(fp);
他のヒント
この例では、ストリームがうまく機能するため、ブーストを使用する必要はありません。
int main(int argc, char* argv[])
{
ifstream file(argv[1]);
const unsigned maxIgnore = 10;
const int delim = ',';
int x,y,z;
vector<int> vecx, vecy, vecz;
while (file)
{
file >> x;
file.ignore(maxIgnore, delim);
file >> y;
file.ignore(maxIgnore, delim);
file >> z;
vecx.push_back(x);
vecy.push_back(y);
vecz.push_back(z);
}
}
ブーストを使用する場合は、 tokenizer を正規表現に...:)
次のようなもの:
vector<int> inint;
vector<int> inbase;
vector<int> outbase;
while (fgets(buf, fh)) {
char *tok = strtok(buf, ", ");
inint.push_back(atoi(tok));
tok = strtok(NULL, ", ");
inbase.push_back(atoi(tok));
tok = strtok(NULL, ", ");
outbase.push_back(atoi(tok));
}
エラーチェックを除きます。
std :: getlineを使用すると、テキストの行を読み取ることができ、文字列ストリームを使用して個々の行を解析できます。
string buf;
getline(cin, buf);
stringstream par(buf);
char buf2[512];
par.getline(buf2, 512, ','); /* Reads until the first token. */
テキスト行を文字列に入れると、sscanf(buf.c_str()、&quot;%d、%d '%d&quot ;,&amp; i1、&ampなど、必要な解析関数を実際に使用できます; i2、&amp; i3)、部分文字列で整数を使用してatoiを使用するか、他の方法を使用します。
入力ストリーム内の不要な文字が存在することがわかっている場合は、無視することもできます。
if (cin.peek() == ',')
cin.ignore(1, ',');
cin >> nextInt;
Boostライブラリの使用を気にしない場合...
#include <string>
#include <vector>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
std::vector<int> ParseFile(std::istream& in) {
const boost::regex cItemPattern(" *([0-9]+),?");
std::vector<int> return_value;
std::string line;
while (std::getline(in, line)) {
string::const_iterator b=line.begin(), e=line.end();
boost::smatch match;
while (b!=e && boost::regex_search(b, e, match, cItemPattern)) {
return_value.push_back(boost::lexical_cast<int>(match[1].str()));
b=match[0].second;
};
};
return return_value;
}
ストリームから行を取得し、Boost :: RegExライブラリ(キャプチャグループを使用)を使用して、行から各番号を抽出します。有効な数値ではないものはすべて自動的に無視しますが、必要に応じて変更できます。
#include
を使用した場合はまだ約20行ですが、これを使用して、ファイルの行から本質的に何でもを抽出できます。これは些細な例です。データベースフィールドからタグとオプションの値を抽出するためにほとんど同じコードを使用していますが、唯一の大きな違いは正規表現です。
編集:おっと、3つの独立したベクターが必要でした。代わりにこのわずかな変更を試してください:
const boost::regex cItemPattern(" *([0-9]+), *([0-9]+), *([0-9]+)");
std::vector<int> vector1, vector2, vector3;
std::string line;
while (std::getline(in, line)) {
string::const_iterator b=line.begin(), e=line.end();
boost::smatch match;
while (b!=e && boost::regex_search(b, e, match, cItemPattern)) {
vector1.push_back(boost::lexical_cast<int>(match[1].str()));
vector2.push_back(boost::lexical_cast<int>(match[2].str()));
vector3.push_back(boost::lexical_cast<int>(match[3].str()));
b=match[0].second;
};
};
なぜpythonと同じコードではないのですか?)
std::ifstream file("input_hard.dat");
std::vector<int> inint, inbase, outbase;
while (file.good()){
int val1, val2, val3;
char delim;
file >> val1 >> delim >> val2 >> delim >> val3;
inint.push_back(val1);
inbase.push_back(val2);
outbase.push_back(val3);
}
より難しい入力形式に拡張できるようにしたい場合は、スピリットを考慮し、パーサーコンビネータライブラリを強化する必要があります。
このページには必要なことをほぼ実行する例(ただし、実数と1つのベクトルを使用)