LoginSignup

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 5 years have passed since last update.

文字列・ファイル操作

Last updated at Posted at 2014-02-25

文字列とファイル操作

文字列については、いわゆるC文字列のみ使う。
C文字列のデータ構造は事前に長さが決まってるNULLをデータの終了とする文字型の単方向リストと捉える。

やってみたいこと

文字列操作

・任意の1文字を探す。見つかったらその位置を取得する。
・指定した任意の開始文字と終了文字を含むかどうか。含まれていた場合、
 開始文字と終了文字に挟まれている位置と挟まれていない位置を取得。

ファイル操作

1 行番号の割り振り。つまりユニークで順番に整列されたidの割り振り。
2 区切り文字でトークン化(コンマ区切り、Tab区切り)された入出力。
3 1行中の特定文字列の読み飛ばし、特定文字例以外の読み込み。
4 複数行中の特定文字列の読み飛ばし、特定文字例以外の読み込み。
5 行頭のTabを数える。

1,2,3が出来たらいくつかの項目(フィールド)に対する操作を出来るようにする。
例:

 id:01 [tab] name:nanashi [tab] age:20 [EOL]
 [EOF]

上のようなファイルから文字列"nanashi"だけをストリームにバッファする。

ここから発展させて複数ファイルのマッチング処理。

3,4,5は行頭のTab数を階層構造の深さと見なす。
ここまで出来たらxmlパースも出来そう・・・。

とりあえずは一番理想的(やりやすい)1行、あるいは1つの文字列、シーケンシャルアクセスでやる。

ここで使う言葉

文字
  サイズ1Byte以内で一意にコード化された数値。ここではAsciiコード(ANSI)を使う。
セル
  1つの値を格納する式。単純型の変数など。
レコード
  一まとまりの意味をもつセルの集合。例として構造体。
ファイル
  レコードの集合。ハードディスク、光学ディスク、DAT、紙(印刷物)などメインメモリ以外に書き出す。
ストリーム
  入力装置、出力装置間をバイト単位で行う高レベルなデータの流れ。

idの割り振り

#include <iostream>
#include <fstream>

using namespace std;

const char* tmp_path = "D:\\tmp.txt";
const char newline = 0x0a;

void set_record( const char*, int );
int id_count();

int main()
{
    set_record( "taro", 10 );
    set_record( "jiro", 20 );
    set_record( "siro", 20 );
    set_record( "goro", 20 );

    return 0;
}

// ディスク・ファイルから改行(Lf)の数をかぞえる。
int id_count()
{
    int count = 0;
    char ch = '\0';

    ifstream fs_in ( tmp_path, ios::in | ios::binary );

    // EOFまでLfを探して、見つかった数をカウントアップ。
    while( fs_in.get(ch) ) {
        if( ch == newline ) ++count;
    }
    return count;
}

// id、名前、年齢をディスク・ファイルに書き込む。
void set_record( const char* name, int age )
{
    int id = id_count();

    ofstream fs_out ( tmp_path, ios::out | ios::binary | ios::app );

    fs_out  << id << '\t'
            << name << '\t'
            << age << '\n';
}

φ(`д´)メモメモ...

#include <iostream> 
#include <string>
#include <fstream>
#include <cstdlib>

using namespace std;

const int record_length = 5;
const int name_max = 80;

void prompt();

int main()
{
    prompt();

    return 0;
}

// 個人情報のデータホルダー構造体
struct person
{
    char *name[ record_length ];
    int age[ record_length ];
    char sex[ record_length ];

    person()
    {
        for ( int i = 0; i < record_length; ++i )
        {
            name[i] = '\0';
            age[i] = -1;
            sex[i] = '\0';
        }
    }

};
// 個人情報を入力 名前・年齢・性別
// 年齢は0以上、 性別はfまたまmのどちらかで入力。
void prompt()
{
    char inpt_name[ name_max ]; // 80文字まで
    int inpt_age = -1;
    char inpt_sex = '\0';

    person personal_data;

    // NULLで初期化
    for( int i = 0; i < name_max; ++i ) inpt_name[ i ] = '\0';

    int i = 0;
    // EOFが入力されるまで入力
    while( cin >> inpt_name >> inpt_age >> inpt_sex )
    {
        // 確保済みの配列レコード数(5個)入力されたらループを抜ける。
        if ( i >= record_length - 1 ) break;

        // 年齢が負数の場合、入力値を破棄し、fialbitを立てる。
        // また文字が入力された場合は型チェックで自動的にfailbitが立てられる。
        if ( inpt_age < 0 )
        {
            cout << "age invalid\n";
            inpt_age = -1;
            cin.clear( ios::failbit );
        }
        // 性別がfかmで入力されていなければ、入力値を破棄し、fialbitを立てる。
        if ( inpt_sex != 'f' && inpt_sex != 'm' )
        {
            cout << "inpute \'f\' or \'m\' about sex\n";
            inpt_sex = '\0';
            cin.clear( ios::failbit );
        }
        // 入力値が正当ならデータをコピー。
        if( cin.good() )
        {
            personal_data.name[i] = inpt_name;
            personal_data.age[i] = inpt_age;
            personal_data.sex[i] = inpt_sex;
            ++i;
        }
        // ここまででfailbitが立っていれば、クリアし入力を継続する。
        else if( cin.fail() ) 
        {
            cin.clear();
        }

        /*
            注意:入力チェックは不十分。名前の入力が111であったりしても許可してしまう。
                   名前のオーバフローも未考慮。
        */
}

ちと、書き換え。(途中)

#include <iostream> 
#include <string>
#include <fstream>
#include <cstdlib>

using namespace std;

class Input
{
private:
    string name;
    int age;
    char sex;
    const int recored_length;
public:
    explicit Input( int size ) : recored_length( size )
    {
        if ( size < 0 ) size = 0;
        name = "";
        age = -1;
        sex = '\0';
    }
    bool age_range( int age );
    bool check( char sex );
    void prompt();
    int get_record_length() const;
    void get_personal_data( string&, int&, char& ) const;
};
bool Input::age_range( int age )
{
    if ( 0 < age && age < 100 ) return true;
    else return false;
}
bool Input::check( char sex )
{
    if ( sex == 'm' || sex == 'f' ) return true;
    else return false;
}
void Input::prompt()
{
    int count = 0;
    string inpt_name = "";
    int inpt_age = -1;
    char inpt_sex = '\0';

    while ( cin >> inpt_name >> inpt_age >> inpt_sex )
    {
        if ( !age_range( inpt_age ) ) cout << "invalid age\n";
        else if ( !check( inpt_sex ) ) cout << "input sex \'f\' or \'m\'\n";
        else if ( !cin )    
        {
            cout << "invalid input\n";
            cin.clear();
        }
        else
        {
            name = inpt_name;
            age = inpt_age;
            sex = inpt_sex;
        }
    }
}
int Input::get_record_length() const
{
    return recored_length;
}
void Input::get_personal_data( string& get_name, int& get_age, char& get_sex ) const
{
    get_name = name;
    get_age = age;
    get_sex = sex;
}

int main()
{
    Input data( 5 );
    data.prompt();

    string name;
    int age;
    char sex;

    data.get_personal_data( name, age, sex );

    cout << name << ' ' << age << ' ' << sex << endl;

    return 0;
}
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up