はじまして。
今、CSVファイル(2列XX行)からfgetsで取得後、
strtokで「,」前後を取得して、1列目をキー、2列目を値として
mapにどんどん登録していきたい処理を作りたいのですが、
参考になるのはないですか??
strtokからちょっと使い方が
まだできていないので、
間違ってると思いますが、
ここまで作りました。
#include <stdio.h>
int main(){
FILE *fp;
char buffer[250];
fp = fopen("message.csv","rt");
if( !fp ){
return 1;
}
while( !feof(fp) ){
fgets(buffer,sizeof(buffer),fp);
strtok(buffer,",");
printf("%s",buffer);
}
fclose(fp);
return 0;
}
最終的にはmapにちゃんと登録されているかを確認するため
0x00001 code1
0x00002 code2
0x00003 code3
のような形で確認できたらと思っております。
すいませんがアドバイスお願いします。
strtokを本当に調べられたのでしょうか?
Google検索して一番上に来るサイトにでもサンプルソースが載っていますよ。
mapについては、これまた STL map 等のキーワードでサンプルソースが載っているサイトが見つかります。
# 2列固定ならば sscanfって手もあるかも。
## この手の質問が最近多いような、、、
## http://www.vcppclub.com/bbs1/wforum.cgi?mode=allread&no=2153&page=0
## http://www2.realint.com/cgi-bin/tarticles.cgi?pointc+20821
修正してみました。
#include <stdio.h>
#include <string.h>
int main(){
FILE *fp;
char buffer[250];
char *txt
fp = fopen("message.csv","rt");
if( !fp ){
return 1;
}
while( !feof(fp) ){
fgets(buffer,sizeof(buffer),fp);
txt = strtok(buffer,",");
printf("%c",txt);
}
fclose(fp);
return 0;
}
使い方は正直今調べています。
ただ、参考書等を見ても、
strtokの「,」前と後ろを見て
mapのキーと値に振りあけてmapに登録するイメージが
わかなかったので載せてみました。
もう少しヒントをください。
すいません..
もう一つ質問させてください。
「buffer」っていうのを明示的に値を設定してますが
明示的に設定しない方法はありますか??
>「buffer」っていうのを明示的に値を設定してますが
>明示的に設定しない方法はありますか??
は
>char buffer[250];
の 250 のことをおっしゃっているのでしょうか?
まぁ最初はこれでいい気がします。
(STLのstd::stringを使う手もありますが、とりあえずはこれでOKでは。)
結局 map に入れるときは
キー(文字列)と値(文字列)であることから
文字列の変数を少なくとも2つ用意します。
(用意しなくてもいけるけど明確化するために用意します。)
#include <string>
#include <map>
を追加して、変数宣言に
std::string strKey;
std::string strValue;
std::map< std::string, std::string > mapData;
を追加し、strtokで分割後の文字をそれぞれ
(CSVのデータがどうなっているかわからないので)
例えば、strKey=1つめのトークン,strValue=2つめのトークン
としてからmapDataに追加します。
各処理はネットで調べてください。
そのまま答えをくれる人は相当お人よしか、暇な人しかいません。
あるていど考えてくださいね。
# ここまでやる私もお人よし。。。
> 「buffer」っていうのを明示的に値を設定してますが
> 明示的に設定しない方法はありますか??
読み出す前にあらかじめ読み出し先のサイズが分かればmalloc や new で
動的に確保することは可能ですが、ファイルからの入力など、
事前にサイズが分からないと C では難しいです。
C++ なら、std::ifstream とか std::getline とか std::string とかを
調べてもらうと、バッファサイズを意識しなくて済む方法が見つかると
思います。
今の C言語風の記述とは使う関数が一新してしまいますが.....。
# それならいっそ、strtok も string::find_first_of に...(好みの問題)。
忙しい中、回答ありがとうございます。
もう少しちゃんと調べてから聞きます。
初めて間もないのにこういう処理を
やろうとしたのも間違えかもしれませんが..
再度作ってみます。
Banさん
多分私の話していることはC++になってます。
すいませんでした。
C言語かC++なんて考えてなかった。。。
C++前提でしたね、私の発言は。
お名前とこの手の質問の類だとおもってC++だと思い込んでました。
(C++でよかった。と)
>C++ なら、std::ifstream とか std::getline とか std::string とかを
>調べてもらうと、バッファサイズを意識しなくて済む方法が見つかると
>思います。
>今の C言語風の記述とは使う関数が一新してしまいますが.....。
># それならいっそ、strtok も string::find_first_of に...(好みの問題)。
微妙に混ざっているとなんか個人的にそろえたくなりますね。
すいませんでした。
今、まだ奮闘中でありますが、
検索して調べてもちょっとまだ言葉が難しかったりするので
理解するまで時間かかりそうです。
(正直だんだん意味が分からなくなってきています)
mapにまでどうつなげればいいのかが
整理できてません。
bufferをなくすってことは、
fgetsが使えなくなるのか等??
もう少しがんばってみます。
> bufferをなくすってことは、
> fgetsが使えなくなるのか等??
fgets というのは元々 (C++ ではなく) C の標準関数なのです。
# そもそも<…….h>というヘッダは、C 言語互換の古いヘッダですし...。
C 言語ではプログラマが明示的に上限を指定して読み出し、
長さ超過時の処理を自分で決めるのが基本なのです。
なので、fgets を使っている限り、読み出しサイズに合わせてメモリを
確保してくれたりはしません。領域だけ可変にしても、fgets の引数は
サイズ固定のままですし、自分で fgets のループなどを下記順次
読み出ししていくしかありません。
一方、C++ から新しく用意された標準ライブラリ(含むSTL)は、
その辺りの管理を内部でやってくれますから、buffer のサイズも
意識せず、単に std::string buffer; とか書けばよくなりますが、
C 互換の fgets 等とは相性があまりよくありません。
C++ には C++ 用の getline などが別途あるので、のきなみこれらに
変更する必要が出てきます。
# 必ずしも全部置き換える必要があるわけではないですが、混ぜると
# 可読性が落ちたり、要らない注意点が増えたりしかねません。
# map とか出てますし(他の質問も含め)C++ だとは思っていたんですが、
# (C++ でも) C 互換の関数で実現できないことはありませんし、
# あまりに皆さん同じC言語風コードを提示されるので課題の指定/制限が
# あるのかなぁと思いまして。
# 余談
# VC 等、一部のコンパイラには独自拡張で追加されてたりしますが、
# 標準 C++ の fpen に "t" というモードはありません。
# 基本的には "b" でなければ "t" です。
指摘ありがとうございます。
何度も読み直してますが..
全部C++の標準ライブラリにするってことですか??
そうなるとちょっと私にはまだ難しい気もします。
やっとfgetsまでたどり着けたので..
ちょっと難しいので頭を冷やしてから
再度考えます
> 全部C++の標準ライブラリにするってことですか??
buffer のサイズを可変にしようと思うと結果的にその方が楽かなぁと。
ただ、そんなことにこだわらず、cvs -> std::map への格納を
実現するだけならとりあえず今の fgets でも実現は可能です。
C++への切り替えは大変だろうと思うので、無理にいきなり手を出さず、
まずは今の形で動くものを仕上げてからのことでいいと思います。
Blue さんも先の回答でそう答えられてます。
> まぁ最初はこれでいい気がします。
> (STLのstd::stringを使う手もありますが、とりあえずはこれでOKでは。)
> ただ、そんなことにこだわらず、cvs -> std::map への格納を
cvs(バージョン管理システム)じゃないですね、
csv(カンマ区切り値)ですね。
ついでに、先の fpen も fopen の typo です...........orz
色々ありがとうございます。
まずはfgetsを使用してやりたいと思います。
っとなるとstrtok()の使い方を上手く使わないと
できないのでがんばります。
イメージとしては、
2列XX行のファイルにしているので
,の前と後用の変数を用意して、
mapの変数??keyとvalueに登録していくって形に
なるのではと思ってます。
while( !feof(fp) ){
fgets(buffer,sizeof(buffer),fp);
txt = strtok(buffer,",");
printf("%c",txt);
}
の部分を修正しないとって思ってますので
もう少しがんばってみます。
すいません..。
結局やってはみたのですが、
恥ずかしながらちょっとまだ悩んでいます。
もう少しヒントかなにかください。
(もうないとは思いますが..)
で、結局、strtokを調べろと言われたところまで戻ることになったと思いますが、
ちゃんと調べたのですか?
何がどのようにわからないのですか?
わからない、わからないだけでは、何も進みませんよ。
>結局やってはみたのですが、
>恥ずかしながらちょっとまだ悩んでいます。
やっているというのが伝わってきません。
出来たとこまでのソースを提示してください。
このままでは、他力でこの問題を解決しようという風にしかみえません。
>strtokを本当に調べられたのでしょうか?
>Google検索して一番上に来るサイトにでもサンプルソースが載っていますよ。
>mapについては、これまた STL map 等のキーワードでサンプルソースが載っているサイトが見つかります。
はやりましたか?
そんなに難しい問題ではないのですが。
一応下に今までのヒント+あなたのソースをC++風に書き換えたものを書きます。
コメント部分は自力で検索して解決してください。
#include <cstdio>
#include <cstring>
#include <map>
#include <string>
static const size_t BUFF_SZIE = 250;
int main( void )
{
FILE* fp;
char buffer[ BUFF_SZIE ];
char* pszToken;
std::string strKey;
std::string strValue;
std::map< std::string, std::string > mapData;
fp = fopen( "message.csv", "r" );
if ( !fp ) return EXIT_FAILURE;
while ( !feof( fp ) )
{
fgets( buffer, sizeof( buffer ), fp );
pszToken = strtok( buffer, "," ); // ここのpszTokenがKey!
// strKeyに代入
// ■トークン分割処理
// 2つ目ののトークンがValue
// strKeyとstrValueをセットでmapDataに追加
}
fclose( fp );
// 任意のKeyでValueを表示
return EXIT_SUCCESS;
}
そうですね。
すいません。
上記ソースのように
,を見て一つずつ出力させること
(tokenとなるtxtがNULLかどうかを見る処理を追加済み)は
理解できたのですが、
応用した変数を二つ使う??
場合の処理のイメージがまだつきません。
考えが間違ってるかもしれませんが..
一度、txtに区切った値を取得後、NULLにする前に
変数a(mapではキー)、変数b(値)と入れていって
txtをNULLにしていくと思います。
間違ってるかもしれませんが..
MSDN ライブラリーの strtok のサンプルです。
これを見て、使い方を勉強してください。
#include <string.h>
#include <stdio.h>
char string[] = "A string\tof ,,tokens\nand some more tokens";
char seps[] = " ,\t\n";
char *token;
int main( void )
{
printf( "Tokens:\n" );
/* Establish string and get the first token: */
token = strtok( string, seps );
while( token != NULL )
{
/* While there are tokens in "string" */
printf( " %s\n", token );
/* Get next token: */
token = strtok( NULL, seps );
}
}
MSDNライブラリー http://www.microsoft.com/japan/msdn/library/
>static const size_t BUFF_SZIE = 250;
> char buffer[ BUFF_SZIE ];
綴りみすってタ。。。orz
BUFF_SIZEですな。(まじあふぉですゎ)
変数名だからまぁなんでもいいんですけどね。
とりあえず、言葉で書くのもいいですが、出来たところまでのソースを提示してください。
待っていても誰も答えをくれませんよ。
色々すいませんでした。
まだできていませんが、
上記を参考にここまで作成してます。
(トークン分割処理はまだできてませんが)
まずは、for文でmapの中の値valueを全て出力させる処理を
作成して、最終的には、ヘッダーファイルを作って
mainに関数を呼び出す形で終わりたいと思ってます。
また何かありましたら、ご指摘等お願いします。
ご迷惑をおかけしました。
#include <stdio.h>
#include <string>
#include <map>
using namespcae std;
int main(){
FILE* fp;
char buffer[250];
//サイズ指定をしているので最終的にはstd::stringを使用したい
char *token;
std::string strKey; //mapキー
std::string strValue; //map値
std::map<std::string, std::string> mapData;
fp = fopen("message.csv","r");
if( !fp ){
return 1;
}
while( !feof(fp) ){
fgets(buffer,sizeof(buffer),fp);
token = strtok(buffer,",");
strKey = token;
strValue = token;
//■トークン分割処理 → まだできていません。
私の考えは、一度token = strtok(NULL,",");を入れると
思ってますが..
mapdata.insert(pair<std::string, std::string>
(strKey,strValue));
token = strtok(NULL,",");
}
fclose(fp);
map<string, string>::iterator p;
for(p = mapdata.begin(); p != mapdata.end(); p++){
printf("%s",p -> second);
}
return 0;
}
大体の流れはあっていると思います。
> //■トークン分割処理 → まだできていません。
> 私の考えは、一度token = strtok(NULL,",");を入れると
> 思ってますが..
いちどそれでやってみてはどうでしょう。
失敗したらそのときはそのときです。
> mapdata.insert(pair<std::string, std::string>
> (strKey,strValue));
ですが、
mapData.insert( std::map< std::string, std::string >::value_type
( strKey, strValue ) );
でも、
mapData[ strKey ] = strValue;
OKです。
こまかいですが、
>map<string, string>::iterator p;
イテレタの指す値を変更しなのであれば、
map< std::string, std::string >::const_iterator p;
のほうがベターです。
while文の中の最後の
>token = strtok(NULL,",");
の処理はなくてもよいでしょう。
それと全体的に、
using namespce std;
としているので、std:: をつけなくてもよいでしょう。
それと、変数名が間違っていますね。
> std::map<std::string, std::string> mapData;
で宣言しているのに
実際使っているのは mapdata になっています。
もしかして、コンパイル環境がないのでしょうか?
周囲の部分ばかりで、肝心のstrtokの部分は、ほとんど進んでいないようですが…
まきじさんの例をよく見て、作って下さい。
# これ以上のヒントはない気もするし。
というか、本当にstrtokの使い方がわからないのであれば、ファイルから読み込む
とかそういう異なる要素は一切排除して、まずはstrtokだけを使うプログラムを
書いてみればいいのに。
色々すいません。
まずはstrtokの部分からですね。
コンパイルの環境は..
すいません。
今はないです。
昼間とかは外出してVisual stdioを使ってやったりしています。
フリーで
Microsoft Visual C++ Toolkit 2003
っていうのが似てるみたいなので
使おうと思ったのですが、コンパイラだけなので
使い勝手がよく分からないってのが現状です。
>フリーで
>Microsoft Visual C++ Toolkit 2003
>っていうのが似てるみたいなので
>使おうと思ったのですが、コンパイラだけなので
>使い勝手がよく分からないってのが現状です。
環境変数、PATH,INCLUDE,LIB を設定するだけだと思いますが。
後はエディタとコマンドプロンプトでできます。
cl hoge.c
という感じで。
やっぱりマルチポストでしたね。。。
http://www.vcppclub.com/bbs1/wforum.cgi?mode=allread&no=2153&page=0
# >fp = fopen("message.csv","r");
# で、カナリあやしいとはおもったし。
マルチポストはなぜ悪いのかはWeb検索でもしてください。
IDE環境がほしいならば、
http://www.vcppclub.com/bbs1/wforum.cgi?mode=allread&no=2156&page=0
の Banさん のあげられたソフトを探してみては。
色々すいませんでした。
最終的にはこんな感じでしょうか。
#include <stdio.h>
#include <string>
#include <map>
using namespcae std;
int main(){
FILE* fp;
char buffer[250];
//サイズ指定をしているので最終的にはstd::stringを使用したい
char *token;
string strKey; //mapキー
string strValue; //map値
map<string, string> mapdata;
fp = fopen("message.csv","r");
if( !fp ){
return 1;
}
while( !feof(fp) ){
fgets(buffer,sizeof(buffer),fp);
token = strtok(buffer,",");
strKey = token;
token = strtok(NULL,"\0");
strValue = token;
mapdata.insert(pair<string, string>(strKey,strValue));
//token = strtok(NULL,",");
}
fclose(fp);
map<string, string>::const_iterator p;
for(p = mapdata.begin(); p != mapdata.end(); p++){
printf("%s",p -> second);
}
p = mapdata.find(strKey);
if(p != mapdata.end()){
printf("%s",p -> second);
}
else {
printf("not_data");
}
return 0;
}
bufferの部分のところは、もう少し修正したいと
思ってます。
色々ありがとうございました
>bufferの部分のところは、もう少し修正したいと
>思ってます。
STLのstd::ifstream(ファイル入力用),std::getline(fgetsの代わり),
std::string::find_first_of(区切り文字検索用)等を調べられるとよいでしょう。
# 私の場合はさらに std::vector を利用して、区切り文字分割処理を関数かして、
# 区切った文字を配列に設定していくようにしますね。
Blueさん回答ありがとうございます。
std::getlineを使えば完成かな??
って思ってます。
色々すいませんでした。
たびたびすいません。
fgetsを使用したい場合は、
std::stringを使用することによって
char buffer[250];の変わりになるという意味合いで..
上記掲示板で。
「解決」にしたのですが
気になったので質問してみました
>fgetsを使用したい場合は、
>std::stringを使用することによって
>char buffer[250];の変わりになるという意味合いで..
とはいってませんよ。
std::stringでfgetsそうとうのことをやりたいには
std::getlineとstd::ifstreamをつかわないといけません。
# fgetsは結局バッファサイズが必要なのです。
std::getlineをしらべられればすぐわかると思いますが。
すいませんでした。
やはりfgetsではバッファサイズ指定が必要って
ことですよね..
何かあるのかと思ってました。
そうなると#defineでサイズを登録して
やろうかなっと思います
(見た目きれいかな??っと思ったので)
もう解決しているので、私が書いたものを
参考にどうぞ。
#pragma warning (disable:4786)
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
void split( const std::string& strLine, std::vector< std::string >& vecData, const std::string& strDelimiter )
{
std::string::size_type nIndex = 0;
std::string::size_type nNext = 0;
std::string::size_type nLength = strLine.length();
std::string::size_type nDelimiterLen = strDelimiter.length();
vecData.clear();
while ( nIndex = strLine.find( strDelimiter, nNext ), nIndex != -1 )
{
vecData.push_back( strLine.substr( nNext, nIndex - nNext ) );
nNext = nIndex + nDelimiterLen;
}
if ( nNext <= nLength )
{
vecData.push_back( strLine.substr( nNext ) );
}
}
int main( void )
{
std::string strLine, strDelimiter( "," );
std::vector< std::string > vecLineData;
std::map< std::string, std::string > mapData;
std::ifstream finData( "test.csv" );
if ( !finData.is_open() )
{
return EXIT_FAILURE;
}
while ( std::getline( finData, strLine ) )
{
split( strLine, vecLineData, strDelimiter );
if ( vecLineData.size() < 2 )
{
continue;
}
mapData[ vecLineData.at( 0 ) ] = vecLineData.at( 1 );
}
std::map< std::string, std::string >::const_iterator it;
for( it = mapData.begin(); it != mapData.end(); it++ )
{
std::cout << it->first << ":" << it->second << std::endl;
}
return EXIT_SUCCESS;
}
環境 OS : WindowsXp SP6 Pro
コンパイラ,IDE : Visula C++6.0 SP6 Stand
> そうなると#defineでサイズを登録して
> やろうかなっと思います
> (見た目きれいかな??っと思ったので)
#defineを使うのは、「見た目がきれい」という趣味的な理由ではなく、プロ
グラム中のあちこちで使われる定数値などを分かりやすい文字列で定義する
ことにより、ソースを分かりやすくするとともに、メンテナンスし易くする
という実用上の理由によります。
例えば、
#define BUFF_SIZE 256
とすれば、256バイトのバッファサイズを全て"BUFF_SIZE"という文字列で表
現できますし、もし、サイズを変更する必要が生じたときにはこの1行を変
更するだけで済みます。
なお、Cよりも型チェックにうるさいC++では、#defineマクロでなく、定数を
使うことを推奨する人もいます(私もそのひとり)。
namespace {
const unsigned BUFF_SIZE = 256;
}
とか。
>環境 OS : WindowsXp SP6 Pro
ありえん環境書いてたw SP2 です。
いちおうカナリ前にC++のソース意識して、
>#include <cstdio>
>#include <cstring>
>#include <map>
>#include <string>
>
>static const size_t BUFF_SZIE = 250;
とかいておいたんですけどね。まぁ必要な部分だけコピられたもようで。。。
namespaceはつけたほうがベターなのかな、ようわからんけど。
私もC++になってもずっと#defineだったので。
> namespaceはつけたほうがベターなのかな、ようわからんけど。
定数の有効範囲を翻訳単位内に限定できますから、やっぱり
可能なら無名名前空間は指定した方がベターだと思います。
(マクロと違って、デフォルトで内部リンケージではありませんから>定数)
# 個人的には(C++において)「マクロは必要悪にすぎない」という
# 方針に賛同してます。
色々ありがとうございました。
Borland5.5C++で確認しました
コンパイラのみなので
コンパイルが通ったところまでしか
確認とれてませんが..
ソースの中で、
mapdata.insert(pair<string, string>(strKey,strValue));
の部分が
pairが未定義のシンボル??
っていう風にエラーが出てしまったので、
mapData[ strKey ] = strValue;
に変更しました。
あとは、ちゃんと任意のstrKeyを
選んだ際、それに対応するstrValueが
呼ばれるかの部分がちゃんとできてるか
確認します。
> mapdata.insert(pair<string, string>(strKey,strValue));
> の部分が
> pairが未定義のシンボル??
> っていう風にエラーが出てしまったので、
名前空間が解決されていないということはないですか?
本当に先に提示されたコードのままだとしたら、
using namespace std; の綴りが間違ってるんですよね。
ただ、もしもこのせいだとすると、
> mapData[ strKey ] = strValue;
に直しても map その他が通るはずがなく。
# それ以外にもそもそも mapData と mapdata の違いで通らない。
> > namespaceはつけたほうがベターなのかな、ようわからんけど。
> 定数の有効範囲を翻訳単位内に限定できますから、やっぱり
> 可能なら無名名前空間は指定した方がベターだと思います。
constであれば無用でしょう。
> (マクロと違って、デフォルトで内部リンケージではありませんから>定数)
Cと違って,C++においてconstは内部結合です。
INCITS/ISO/IEC 14882:2003 3.5 Program and linkage
> 3 A name having namespace scope has internal linkage if it is the name of
(snip)
> - an object or reference that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage;
すいません..
綴りとかは直しております。
ソースは
「namespaceを使用していない場合」
#include <stdio.h>
#include <string>
#include <map>
int main(void){
FILE* fp;
char buffer[250];
char *token;
std::string strKey; //mapキー
std::string strValue; //map値
std::map<std::string, std::string> mapdata;
fp = fopen("message.csv","r");
if( !fp ){
return 1;
}
while( !feof(fp) ){
fgets(buffer,sizeof(buffer),fp);
token = strtok(buffer,",");
strKey = token;
token = strtok(NULL,"\0");
strValue = token;
mapdata[ strKey ] = strValue;
mapdata.insert(pair<string, string>(strKey,strValue));
//token = strtok(NULL,",");
}
fclose(fp);
std::map<std::string, std::string>::const_iterator p;
for(p = mapdata.begin(); p != mapdata.end(); p++){
printf("%s",p -> second);
}
p = mapdata.find(strKey);
if(p != mapdata.end()){
printf("%s",p -> second);
}
else {
printf("not_data");
}
return 0;
}
「使用した場合」
#include <stdio.h>
#include <string>
#include <map>
using namespace std;
int main(void){
FILE* fp;
char buffer[250];
char *token;
string strKey; //mapキー
string strValue; //map値
map<string, string> mapdata;
fp = fopen("message.csv","r");
if( !fp ){
return 1;
}
while( !feof(fp) ){
fgets(buffer,sizeof(buffer),fp);
token = strtok(buffer,",");
strKey = token;
token = strtok(NULL,"\0");
strValue = token;
//mapdata[ strKey ] = strValue;
mapdata.insert(pair<string, string>(strKey,strValue));
//token = strtok(NULL,",");
}
fclose(fp);
map<string, string>::const_iterator p;
for(p = mapdata.begin(); p != mapdata.end(); p++){
printf("%s",p -> second);
}
p = mapdata.find(strKey);
if(p != mapdata.end()){
printf("%s",p -> second);
}
else {
printf("not_data");
}
return 0;
}
mapdata.insertの部分は綴りが間違っていたので
コンパイルできてませんでした。
p = mapdata.find(strKey);
if(p != mapdata.end()){
printf("%s",p -> second);
}
else {
printf("not_data");
}
の部分がちゃんと動くかを
確認したいので
関数を作って、main側で
引数からstrKeyを見て
該当するstrValueを出力できるかどうかを
テストするため、修正しようと思っております。
指摘等ありましたら、お願い致します
>「namespaceを使用していない場合」
> mapdata.insert(pair<string, string>(strKey,strValue));
pairもSTLライブラリのクラスです。std::pairとしてください。
> 指摘等ありましたら、
致命的な点:
1. ファイルからの読み込み終了の判定
while( !feof(fp) ){
fgets(buffer,sizeof(buffer),fp);
...
}
feofはファイル終端に達しているかどうかを「終端指示子」と言われるもの
のON/OFFによって判定します。
ところが、fgetsで最後の行を読み込んだときにはまだ終端指示子はOFFで、
次にfgetsなどで読み込もうとしたとき(読み込みに失敗したとき)に終端指
示子がONとなり、はじめてfeofでファイル終端に達したことが判定できます。
このため、上のコードだと、最後の行の読み込みの後、もう一回fgets以下が
実行されてしまいます。
終端に達したファイルに対してfgetsを実行するとbufferにはNULLが返ります
から、それ以降の処理がおかしくなります。
この場合、ファイル終端の判断はfgetsの戻り値を使うのが妥当と思います。
while( fgets( buffer, sizeof(buffer), fp ) != NULL ){
...
}
2. printfの引数
printf( "%s", p->second );
mapdataの第2要素の型はstd::stringなので、それをこの書式で出力するこ
とはできません。
コンパイルできてしまうかも知れませんが、実行結果はどうなることやら...
std::stringクラスには、Cスタイル文字列(const char*)を返すメンバ関数
"c_str"がありますので、それを使いましょう
printf( "%s", p->second.c_str() );
あと、致命的ではありませんが、
token = strtok(NULL,"\0");
fgets関数は行末の改行文字'\n'もバッファに格納しますから、これだと、改
行文字も含めてmapdataの第2要素に格納されます。
そういう仕様であれば良いのですが、改行文字は格納したくないということ
であれば、
token = strtok( NULL, "\n" );
とするのが良いと思います。
以上、ざっと眺めて気がついた点を指摘させていただきました。
> YuO さん
> Cと違って,C++においてconstは内部結合です。
混同してました....orz
ご指摘感謝です。
整理しました。
#include <stdio.h>
#include <string>
#include <map>
int main(void){
FILE* fp;
char buffer[250];
char *token;
std::string strKey; //mapキー
std::string strValue; //map値
std::map<std::string, std::string> mapdata;
fp = fopen("message.csv","r");
if( !fp ){
return 1;
}
while( fgets( buffer, sizeof(buffer), fp ) != NULL ){
fgets(buffer,sizeof(buffer),fp);
token = strtok(buffer,",");
strKey = token;
token = strtok( NULL, "\n" );
strValue = token;
mapdata[ strKey ] = strValue;
mapdata.insert(std::pair<std::string, std::string>
(strKey,strValue));
}
fclose(fp);
std::map<std::string, std::string>::const_iterator p;
for(p = mapdata.begin(); p != mapdata.end(); p++){
printf( "%s", p->second.c_str() );
}
p = mapdata.find(strKey);
if(p != mapdata.end()){
printf( "%s", p->second.c_str() );
}
else {
printf("not_data");
}
return 0;
}
今はmainに書いてある処理を
関数をrenshu.hというヘッダーファイルを作って
mainにincludeさせて簡単な単体テストをやりたいのですが、
int main(void)にした場合、
再度引数を定義しないといけないでしょうか??
>今はmainに書いてある処理を
>関数をrenshu.hというヘッダーファイルを作って
>mainにincludeさせて簡単な単体テストをやりたいのですが、
>
>int main(void)にした場合、
>再度引数を定義しないといけないでしょうか??
日本語が変で意味が良く分かりません。ご自分で読み直してみましたか?
再度引数を定義?
分かることは・・
ヘッダーファイルに関数を移動させるのは良くありません。
ヘッダーファイルは、関数(や変数など)の宣言を書くためのものです。
関数定義はソースファイル(一般的には拡張子cpp)に書いて下さい。
単に関数を増やしたいだけであれば、別にファイルを増やす必要はありません。
mainと別のファイルにしたい理由があれば、別のソースファイル(例えばrenshu.cpp)に書いて下さい。その時にrenshu.hにその関数の宣言だけを書く方法もあります。
ツイート | ![]() |