std::stringによる文字列,テキストファイル処理などに関するサンプルコード



インクルードファイル

このページのコードはstringをインクルードしていることが前提である.

#include <string>

一部の処理はsstreamも使っている.

#include <sstream>

また,名前空間stdは省略できるように以下を宣言しておく.

using namespace std;

基本的な処理

stringへの変換

sstreamで様々な型の変数をstringへ変換する.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12

template<typename T>
inline string RX_TO_STRING(const T &x)
{
    stringstream ss;
    ss << x;
    return ss.str();
}

"<<"オペレータ

上記のRX_TO_STRINGを使ってstringに"<<"オペレータを定義.

  1
  2
  3
  4
  5
  6
  7
template<typename T>
inline string &operator<<(string &cb, const T &a)
{
    cb += RX_TO_STRING(a);
    return cb;
}

小文字化

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16

inline void StringToLower(string &str)
{
    string::size_type i, size;
 
    size = str.size();
 
    for(i = 0; i < size; i++){
        if(str[i] >= 'A' && str[i] <= 'Z') str[i] += 32;
    }
 
    return;
}

数値判定

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27

inline bool IsInteger(const string &str)
{
    if(str.find_first_not_of("-0123456789 \t") != string::npos) {
        return false;
    }
 
    return true;
}
 

inline bool IsNumeric(const string &str)
{
    if(str.find_first_not_of("-0123456789. Ee\t") != string::npos) {
        return false;
    }
 
    return true;
}

空白削除

文字列に含まれるすべての空白(半角/全角スペース,タブ)を削除

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11

inline void DeleteSpace(string &buf)
{
    size_t pos;
    while((pos = buf.find_first_of()) != string::npos){
        buf.erase(pos, 1);
    }
}

先頭の空白のみを削除する場合.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12

inline void DeleteHeadSpace(string &buf)
{
    size_t pos;
    while((pos = buf.find_first_of()) == 0){
        buf.erase(buf.begin());
        if(buf.empty()) break;
    }
}

文字列カウント

ある文字列が別の文字列中に含まれている数.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29

inline int CountString(string &s, int offset, string c)
{
    int count = 0;
    size_t pos0 = offset, pos = 0;
    int n = (int)c.size();
 
    while((pos = s.find(c, pos0)) != string::npos){
        if(pos != pos0){
            count++;
        }
        else{
            s.erase(s.begin()+pos);
        }
        pos0 = pos+n;
    }
 
        if(s.rfind(c) == s.size()-n){
        count--;
    }
 
    return count;
}

ワイド文字列とマルチバイト文字列の相互変換

std::stringとstd::wstringの変換.エントリ関数の最初の方で

setlocale(LC_CTYPE, "JPN");

などとしてロケールを設定しておくこと.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
#include <string>
#include <stdlib.h>
 

inline std::string W2S(const std::wstring &src)
{
    std::string dst;
    size_t mbsize = src.length()*MB_CUR_MAX+1;        char *mbs = new char[mbsize];
    wcstombs(mbs, src.c_str(), mbsize);
    dst = mbs;
    delete [] mbs;
    return dst;
}
 

inline std::wstring S2W(const std::string &src)
{
    std::wstring dst;
    size_t wcsize = src.length()+1;
    wchar_t *wcs = new wchar_t[wcsize];
    mbstowcs(wcs, src.c_str(), wcsize);
    dst = wcs;
    delete [] wcs;
    return dst;
}

ファイル名処理

フルパスからファイル名を抽出

パス区切り"\"と"/"の両方に対応

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21

inline string GetFileName(const string &path)
{
    size_t pos1;
 
    pos1 = path.rfind('\\');
    if(pos1 != string::npos){
        return path.substr(pos1+1, path.size()-pos1-1);
    }
 
    pos1 = path.rfind('/');
    if(pos1 != string::npos){
        return path.substr(pos1+1, path.size()-pos1-1);
    }
 
    return path;
}

フルパスからフォルダパスを抽出

パス区切り"\"と"/"の両方に対応.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45

inline string GetFolderPath(const string &path)
{
    size_t pos1;
 
    pos1 = path.rfind('\\');
    if(pos1 != string::npos){
        return path.substr(0, pos1+1);
        
    }
 
    pos1 = path.rfind('/

フルパスから拡張子を抽出

パス区切り"\"と"/"の両方に対応.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29

inline string GetExtension(const string &path)
{
    string ext;
    size_t pos1 = path.rfind('.');
    if(pos1 != string::npos){
        ext = path.substr(pos1+1, path.size()-pos1);
        string::iterator itr = ext.begin();
        while(itr != ext.end()){
            *itr = tolower(*itr);
            itr++;
        }
        itr = ext.end()-1;
        while(itr != ext.begin()){                if(*itr == 0 || *itr == 32){
                ext.erase(itr--);
            }
            else{
                itr--;
            }
        }
    }
 
    return ext;
}

ファイル名から拡張子を削除

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14

inline string ExtractPathWithoutExt(const string &fn)
{
    string::size_type pos;
    if((pos = fn.find_last_of(".")) == string::npos){
        return fn;
    }
 
    return fn.substr(0, pos);
}

ファイル名を抽出(拡張子を除くフラグ付き)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
string ExtractFileName(const string &path, bool without_extension = true)
{
    string fn;
    string::size_type fpos;
    if((fpos = path.find_last_of("/")) != string::npos){
        fn = path.substr(fpos+1);
    }
    else if((fpos = path.find_last_of("\\")) != string::npos){
		fn = path.substr(fpos+1);
	}
	else{
		fn = path;
	}
 
	if(without_extension && (fpos = fn.find_last_of(".")) != string::npos){
		fn = fn.substr(0, fpos);
	}
 
	return fn;
}

フォルダ区切り位置の検索

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30

inline bool FindPathBound(const string &str, std::string::size_type &pos)
{
    std::string::size_type pos0, pos1;
    pos0 = str.find_last_of("\\");
	pos1 = str.find_last_of("/");
 
	if(pos0 == std::string::npos){
		if(pos1 == std::string::npos){
			return false;
		}
		else{
			pos = pos1;
		}
	}
	else{
		if(pos1 == std::string::npos){
			pos = pos0;
		}
		else{
			pos = (pos0 < pos1) ? pos0 : pos1;
		}
	}
 
	return true;
}

テキストファイル処理

テキストファイル行処理

getlineでテキストファイルの各行を取り出して処理を行うサンプル. コメント行や空行はスキップする.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39

bool Read(string file_name)
{
    ifstream file;
 
    file.open(file_name.c_str());
    if(!file || !file.is_open() || file.bad() || file.fail()){
        cout << "Read : Invalid file specified" << endl;
        return false;
    }
 
    string buf;
    string::size_type comment_start = 0;
    while(!file.eof()){
        getline(file, buf);
 
                if( (comment_start = buf.find('#')) != string::size_type(-1) )
            buf = buf.substr(0, comment_start);
 
                DeleteHeadSpace(buf);
 
                if(buf.empty())
            continue;
        
         
    }
 
    file.close();
 
    return true;
}

各行の文字列処理に使えそうな関数を以下に挙げる.

カンマ区切り文字列からの要素抽出

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48

inline int GetNextString(const string &src, string &sub, int pos)
{
    bool extracted = false;
    if(src[pos] == '\"'){            size_t j = src.find("\"", pos+1);
        if(j != string::npos){
            sub = src.substr(pos+1, j-(pos+1));
            pos = j+1;
            extracted = true;
        }
    }
 
    size_t i = src.find(",", pos);
    if(i == string::npos){        
        if(!extracted) sub = src.substr(pos, string::npos);
        return (int)string::npos;
    }
    else{
        int cnt = 1;
        while(src[i+cnt] == ' '){                cnt++;
        }
        if(!extracted) sub = src.substr(pos, i-pos);
        return (int)(i+cnt >= src.size() ? (int)string::npos : i+cnt);
    }
}
 
 

inline int GetFirstString(const string &src, string &sub)
{
    return GetNextString(src, sub, 0);
    //int i = (int)src.find_first_of(", ");
    //sub = src.substr(0, i);
    //return i+2;
}
  • 使用例
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
    
    #include <iostream>
    #include <fstream>
    #include <string>
    using namespace std;
    int main(void)
    {
        ifstream file;
     
        file.open("test.csv");
        if(!file || !file.is_open() || file.bad() || file.fail()){
            return 1;
        }
     
        string buf;
        string::size_type comment_start = 0;
        while(!file.eof()){
            getline(file, buf);
     
                    if( (comment_start = buf.find('#')) != string::size_type(-1) )
                buf = buf.substr(0, comment_start);
     
                    if(buf.empty())
                continue;
     
            string sub;
            int pos = 0;
            do{
                pos = GetNextString(buf, sub, pos);
                cout << sub << endl;
            }while(pos != string::npos);
            cout << endl;
        }
        
        file.close();
        return 0;
    }
    test.csvファイルの内容が以下であった場合,
    # カンマ区切りテキスト
    1, 23, 456,789,  10
    
    "double quotation", abc,"defg, hijk"
    出力は以下となる.
    1
    23
    456
    789
    10
    
    double quotation
    abc
    defg, hijk

文字列からベクトル

"(0, 1, 2)"のように書かれた文字列からベクトル要素を取り出す. 3次元ベクトル,2次元ベクトルについてのサンプルを示す. 以下のサンプルでは,3 or 2次元ベクトルを表すクラスVec3,Vec2を使っている(各要素のアクセスはオペレータ"[]"で行う).

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77

inline int StringToVec3(const string &s, Vec3 &v)
{
    int vcount = 0;
    size_t pos;
    v = Vec3(0.0);
    if((pos = s.find('(')) != string::npos){
        while(pos != string::npos && vcount < 3){
            size_t pos1 = pos;
            if((pos1 = s.find(',', pos+1)) != string::npos){
                v[vcount] = atof(s.substr(pos+1, (pos1-(pos+1))).c_str());
                vcount++;
                pos = pos1;
            }
            else if((pos1 = s.find(')', pos+1)) != string::npos){
                v[vcount] = atof(s.substr(pos+1, (pos1-(pos+1))).c_str());
                vcount++;
                break;
            }
            else{
                break;
            }
        }
    }
    if(vcount < 3){
        for(int i = vcount; i < 3; ++i){
            v[i] = v[vcount-1];
        }
    }
 
    return vcount;
}
 

inline int StringToVec2(const string &s, Vec2 &v)
{
    int vcount = 0;
    size_t pos;
    v = Vec2(0.0);
    if((pos = s.find('(')) != string::npos){
        while(pos != string::npos && vcount < 2){
            size_t pos1 = pos;
            if((pos1 = s.find(',', pos+1)) != string::npos){
                v[vcount] = atof(s.substr(pos+1, (pos1-(pos+1))).c_str());
                vcount++;
                pos = pos1;
            }
            else if((pos1 = s.find(')', pos+1)) != string::npos){
                v[vcount] = atof(s.substr(pos+1, (pos1-(pos+1))).c_str());
                vcount++;
                break;
            }
            else{
                break;
            }
        }
    }
    if(vcount < 2){
        for(int i = vcount; i < 2; ++i){
            v[i] = v[vcount-1];
        }
    }
 
    return vcount;
}

文字列からベクトル(汎用版)

上記の2/3次元ベクトル版をより汎用的に使えるように,

  • 多次元ベクトルに対応
  • 文字列中に複数のベクトルがある場合にも対応
  • 区切り文字を呼び出し側が指定 にしたもの.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49

template<class T> 
inline int ExtractVector(string data, const string &end_str, const string &brk_str, int min_elems, 
                         vector< vector<T> > &vecs)
{
    data += end_str;        int n = 0;
    size_t cpos[2] = {0, 0};
    while((cpos[1] = data.find(end_str, cpos[0])) != string::npos){
                string sub = data.substr(cpos[0], cpos[1]-cpos[0]);
        if(sub.empty()){
            cpos[0] = cpos[1]+end_str.size();
            break;
        }
 
                sub += brk_str;
        vector<T> val;
        size_t spos[2] = {0, 0};
        while((spos[1] = sub.find(brk_str, spos[0])) != string::npos){
            string val_str = sub.substr(spos[0], spos[1]-spos[0]);
            DeleteSpace(val_str);
            if(val_str.empty()){
                spos[0] = spos[1]+brk_str.size();
                continue;
            }
 
            val.push_back((T)atof(val_str.c_str()));
            spos[0] = spos[1]+brk_str.size();
        }
        if((int)val.size() >= min_elems){
            vecs.push_back(val);
            n++;
        }
        cpos[0] = cpos[1]+end_str.size();
    }
 
    return n;
}

バイナリファイル

バイナリファイルからの文字列入力

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61

template<class T> 
inline T ReadBinary(ifstream &file)
{
    T data;
    file.read((char*)&data, sizeof(T));
    return data;
}
 

template<class T> 
inline T ReadBinary(ifstream &file, int byte)
{
    T data = 0;
    file.read((char*)&data, byte);
    return data;
}
 

inline int ReadInt(ifstream &file)
{
    int data = 0;
    file.read((char*)&data, 2);
    return data;
}
 

inline bool ReadString(ifstream &file, string &name, int max_size)
{
    char c = ReadBinary<char>(file);
    if((int)c == 0) return false;
 
    name.clear();
    name.push_back(c);
    do{
        c = ReadBinary<char>(file);
        name.push_back(c);
    }while(((int)c != 0) && (max_size == -1 || (int)name.size() < max_size));
    name.push_back((char)0);
 
    return true;
}

時刻

hh:mm:ss形式への変換

0付き数値の作成にちょっとした関数のGenZeroNoを使っている.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40

inline string GenTimeString(double sec, bool use_msec = false)
{
    long value = (int)(1000*sec+0.5);     
    unsigned int h = (unsigned int)(value/3600000);        value -= h*3600000;
    unsigned int m = (unsigned int)(value/60000);        // 分
    value -= m*60000;
    unsigned int s = (unsigned int)(value/1000);            value -= s*1000;
    unsigned int ms = (unsigned int)(value);             
    stringstream ss;
    if(h > 0) ss << GenZeroNo(h, 2) << ":";
    ss << GenZeroNo(m, 2) << ":";
    ss << GenZeroNo(s, 2);
    if(use_msec) ss << "." << GenZeroNo(ms, 3);
 
    return ss.str();
}
 

inline string GenTimeString(int h, int m, int s)
{
    stringstream ss;
    if(h > 0) ss << GenZeroNo(h, 2) << ":";
    ss << GenZeroNo(m, 2) << ":";
    ss << GenZeroNo(s, 2);
    return ss.str();
}

トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2022-11-30 (水) 13:48:11