1

私はPythonで幼虫の段階にあり、C ++で卵前の段階にありますが、特に「Don'tRepeatYourself」の原則で最善を尽くそうとしています。

開くマルチチャネルRAWファイル形式があります。メインのASCIIヘッダーには、文字列と整数として表現できるフィールドがあります(常に空白で埋められた文字としてコード化されています)。2番目の部分はN個のヘッダーで、Nはメインヘッダーのフィールドであり、これらの各ヘッダーには、実際の16ビットマルチチャネルストリームの長さとサイズを参照する、より多くのテキストフィールドと数値フィールド(ASCIIとしてコード化)があります。ファイルの残りの部分を構成します。

これまでのところ、C++でこの動作するコードがあります。

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <map>

using namespace std;

struct Header {
    string version;
    string patinfo;
    string recinfo;
    string start_date;
    string start_time;
    int header_bytes;
    string reserved;
    int nrecs;
    double rec_duration;
    int nchannels;
};

struct Channel {
    string label;
    string transducertype;
    string phys_dim;
    int pmin;
    int pmax;
    int dmin;
    int dmax;
    string prefiltering;
    int n_samples;
    string reserved;
};


int main()
{
    ifstream edf("/home/helton/Dropbox/01MIOTEC/06APNÉIA/Samples/Osas2002plusQRS.rec", ios::binary);

    // prepare to read file header
    Header header;
    char buffer[80];

    // reads header fields into the struct 'header'
    edf.read(buffer, 8);
    header.version = string(buffer, 8);

    edf.read(buffer, 80);
    header.patinfo = string(buffer, 80);

    edf.read(buffer, 80);
    header.recinfo = string(buffer, 80);

    edf.read(buffer, 8);
    header.start_date = string(buffer, 8);

    edf.read(buffer, 8);
    header.start_time = string(buffer, 8);

    edf.read(buffer, 8);
    stringstream(buffer) >> header.header_bytes;

    edf.read(buffer, 44);
    header.reserved = string(buffer, 44);

    edf.read(buffer, 8);
    stringstream(buffer) >> header.nrecs;

    edf.read(buffer,8);
    stringstream(buffer) >> header.rec_duration;

    edf.read(buffer,4);
    stringstream(buffer) >> header.nchannels;

    /*
    cout << "'" << header.version << "'" << endl;
    cout << "'" << header.patinfo << "'" << endl;
    cout << "'" << header.recinfo << "'" << endl;
    cout << "'" << header.start_date << "'" << endl;
    cout << "'" << header.start_time << "'" << endl;
    cout << "'" << header.header_bytes << "'" << endl;
    cout << "'" << header.reserved << "'" << endl;
    cout << "'" << header.nrecs << "'" << endl;
    cout << "'" << header.rec_duration << "'" << endl;
    cout << "'" << header.nchannels << "'" << endl;
    */

    // prepare to read channel headers
    int ns = header.nchannels; // ns tells how much channels I have
    char title[16]; // 16 is the specified length of the "label" field of each channel

    for (int n = 0; n < ns; n++)
    {
        edf >> title;
        cout << title << endl; // and this successfully echoes the label of each channel
    }


    return 0;
};

私がすでにしなければならないいくつかの発言:

  • フォーマット仕様が非常にハードコーディングされているため、構造体を使用することにしました。
  • 読み取るバイト数とタイプはかなり恣意的であるように思われたため、メインヘッダーフィールドを反復処理しませんでした。
  • 各チャネルのラベルを取得できたので、実際には各チャネルのフィールドの構造体を作成します。これ自体は、おそらくマップに保存する必要があります。

私の(うまくいけば簡単な)質問は次のとおりです。

「この種のコードをより「Pythonic」(より抽象的で反復性の低いもの)にするために手抜きをすることを心配する必要がありますか、それともこれはC ++での動作方法ではありませんか?」

多くのPythonエバンジェリスト(私はそれが大好きなので、私自身もそうですが)は、その使いやすさなどを強調しています。ですから、私はしばらくの間、愚かなことをしているのか、それとも正しいことだけをしているのか疑問に思いますが、C ++の性質上、それほど「自動」ではありません。

読んでくれてありがとう

ヘルトン

4

5 に答える 5

5

PythonicC++コードのようなものはないと思います。DRYの原則は両方の言語に適用されますが、「Pythonic」と見なされるものの多くは、Python固有の構造を使用して、Pythonでロジックを表現するための最短で最も甘い方法です。慣用的なC++はまったく異なります。

lambdaたとえば、Pythonicとは見なされず、他のソリューションが存在しない場合のために予約されている場合がありますが、C++標準に追加されているだけです。C ++には、非常にPythonicなキーワード引数がありません。C ++プログラマーは、map必要のないときにを構築することを好みませんが、Pythonプログラマーはdict、効率的な代替手段よりも意図を明確にするために、たまたま多くの問題を投げかける可能性があります。

入力を保存したい場合は、前に投稿した関数を使用してから、次のようにします。

header.version = read_field(edf, 8);
header.patinfo = read_field(edf, 80);

それはあなたにかなりの数の行を節約するはずです。しかし、これらの数行よりも重要なのは、わずかなモジュール性を実現したことです。フィールドの読み取り方法と読み取るフィールドが、プログラムの個別の部分になりました。

于 2011-04-14T20:26:41.820 に答える
2

あなたは正しいです: 書かれているように、コードは反復的です (そしてエラーチェックはありません)。読み取る各フィールドでは、読み取るデータの種類に応じて、実際には 3 つまたは 5 つの手順を実行する必要があります。

  1. ストリームからフィールドを読み取る
  2. 読み取りが成功したことを確認する
  3. データを解析する (必要な場合)
  4. 解析が成功したことを確認します (必要な場合)
  5. ターゲットの場所にデータをコピーします

コードの繰り返しを少なくするために、これら 3 つすべてを関数にまとめることができます。たとえば、次の関数テンプレートについて考えてみます。

template <typename TStream, typename TResult>
void ReadFixedWidthFieldFromStream(TStream& str, TResult& result, unsigned sz) 
{
    std::vector<char> data(sz);

    if (!str.read(&data[0], sz))
        throw std::runtime_error("Failed to read from stream");

    std::stringstream ss(&data[0]);
    if (!(ss >> result))
        throw std::runtime_error("Failed to parse data from stream");
}

// Overload for std::string:
template <typename TStream>
void ReadFixedWidthFieldFromStream(TStream& str, std::string& result, unsigned sz) 
{
    std::vector<char> data(sz);

    if (!str.read(&data[0], sz))
        throw std::runtime_error("Failed to read from stream");

    result = std::string(&data[0], sz);
}

これで、コードをより簡潔にすることができます。

ReadFixedWidthFieldFromStream(edf, header.version, 8);
ReadFixedWidthFieldFromStream(edf, header.patinfo, 80);
ReadFixedWidthFieldFromStream(edf, header.recinfo, 80);
// etc.
于 2011-04-14T20:22:38.777 に答える
1

このコードは単純明快で、理解しやすいものです。機能している場合は、時間を無駄にしないでください。最初に修正する必要がある、書き方が悪く、複雑で、理解しにくい (そしておそらく間違っている) コードがたくさんあると確信しています :)

于 2011-04-14T20:21:26.073 に答える
0

ファイルから直接文字列で読み取るには、この質問The rest is wrongを参照してください。しかし、個人的には、これを行うためのより良い/よりクリーンな方法があると思います。

構造体のサイズが文字列を使用しないことがわかっている場合は、プリミティブ C 型を使用します (構造体がパックされていることを確認してください)。次のリンクを参照してください: http://msdn.microsoft.com/en-us/library/2e70t5y1(v=vs.80).aspx & http://gcc.gnu.org/onlinedocs/gcc-3.2.3/gcc /Type-Attributes.html

たとえば、次のようにします(各文字列のサイズはわかりませんが、アイデアはわかります):

struct Header {
    char version[8];
    char patinfo[80];
    char recinfo[80];
    char start_date[8];
    char start_time[8];
    int header_bytes;
    char reserved[44];
    int nrecs;
    double rec_duration;
    int nchannels;
};

パックされた構造を取得したら、ファイルから直接読み取ることができます。

struct Header h;
edf.read(&h,sizeof(struct Header));

私にとってはこれが最もクリーンな方法ですが、メモリ内の構造体がファイルに保存された構造体と同じサイズであることが保証されるように、構造体をパックする必要があることを忘れないでください。テスト。

于 2011-04-14T20:32:33.303 に答える
0

Zen of Python では、DRY について明示的に言及していません。

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
于 2011-04-14T20:36:36.437 に答える