顯示具有 Lv1 class 標籤的文章。 顯示所有文章
顯示具有 Lv1 class 標籤的文章。 顯示所有文章

class//CartridgeFinder

搜尋一整串的Cartridge2(RNA),要怎麼做?

我要找...黑色81點的第23點

在這一串RNA若還有255點的Gamma和白色9點並且以區域排序,那勢必要在裡面大海撈針是一件可怕的事。
在此,建了一個搜尋器,只要打進關鍵字,會重建關鍵字,再使用重建的關鍵字去找你要找的點。
提供的功能:
找關鍵字搜尋,例如「白色」或「9點」的某一個特定的特色找出來。
精準搜尋,例如「白色9點,離邊1/3的第2點...」

下面可以看見,列舉了很多種,其中列舉的第一項(=0的那一項)可以拿它來設定成error的狀態。

CartridgeFinder.h

#ifndef CARTRIDGEFINDER_H
#define CARTRIDGEFINDER_H

enum reColorType{ rCL_NoMatch, rCL_Se, rCL_CrossTalk, ..., rCL_ShortCode };
enum rePtTotalType{ rPT_NoMatch, rPT_Normal, ..., rPT_Center, rPT_Word, rPT_PureDigital };
enum reMrIndexType{ rMI_NoMatch, rMI_Normal, rMI_Dian, rMI_PureDigital };
enum reParaType{ rPM_NoMatch, rPM_NoPara, rPM_Over, rPM_Length, rPM_Welt,..., rPM_PureDigital };

class CartridgeFinder
{
public:
    const BOOL ReconstrKeyWord(CString&, CString&, CString&, CString&);
    const BOOL Reconstr(CString&, CString&, CString&, CString&);
    const BOOL Reconstr(CString&);

private:
    const BOOL color(CString&);
    const reColorType reColor(CString);
    void reColorCrossTalk(CString&);
    void reColorNits(CString&);
    void reColorSe(CString&);
    void reColorShortCode(CString&);
private:
    const BOOL pointTotal(CString&);
    const rePtTotalType rePointTotal(CString);
    void rePointTotalCenter(CString&);
    void rePointTotalWord(CString&);
    void rePointTotalNormal(CString&);
    void rePointTotalPureDig(CString&);
private:
    const BOOL msrIndex(CString&);
     const reMrIndexType reMsrIndex(CString);
    void reMsrIndexDian(CString&);
    void reMsrIndexNormal(CString&);
    void reMsrIndexPureDig(CString&);
private:
    const BOOL para(CString&);
    const reParaType rePara(CString);
    void reParaOver(CString&);
    void reParaLength(CString&);
    void reParaNoPara(CString&);
    void reParaNits(CString&);
    void reParaPureDig(CString&);
private:
    BOOL isNumber(const CString& strResult);
};

#endif

CartridgeFinder.cpp

精準搜尋和關鍵字搜尋的關鍵

這兩個重建關鍵字的邏輯不同,結果也不同。一個是全都要對,另一個是部份對就可以了。有趣的是CString中找關鍵字的技巧的最後有提到關於「搜尋空字串」和「找不到」是兩回事。
  • 「找某關鍵字or空字串」→true
  • 「找某關鍵字and找不到」→false
const BOOL CartridgeFinder::Reconstr(CString& sample)
{
    CString clr = sample;
    CString pt = sample;
    CString mi = sample;
    CString pr = sample;
    if (Reconstr(clr, pt, mi, pr))
    {
        sample.Format("%s%s%s%s", clr, pt, mi, pr);
        return TRUE;
    }
    else
        return FALSE;
}

const BOOL CartridgeFinder::Reconstr( CString& bkColor, CString& PointTotal, 
                                      CString& MsrPointIndex, CString& Parameter)
{
    if (color(bkColor) & pointTotal(PointTotal) & msrIndex(MsrPointIndex) & para(Parameter))
        return TRUE;
    else
        return FALSE;
}

const BOOL CartridgeFinder::ReconstrKeyWord( CString& bkColor, 
                                             CString& PointTotal, CString& MsrPointIndex, CString& Parameter)
{
    if (color(bkColor) | pointTotal(PointTotal) | msrIndex(MsrPointIndex) | para(Parameter))
        return TRUE;
    else
        return FALSE;
}

重建關鍵字

為了判斷關鍵字的準確性,利用CString中找關鍵字的技巧在此對輸入的字做處理,重建成Cartridge2中會生成的字,到時使用==運算就可以找到了。 在此,只用「找出該量測項目位總共有多少點」的部份來示範設計。
const BOOL CartridgeFinder::pointTotal(CString& strResult)
{
    switch(rePointTotal(strResult))
    {
    case rPT_Normal:      rePointTotalNormal(strResult);  return TRUE;
    case rPT_Center:      rePointTotalCenter(strResult);  return TRUE;
    case rPT_PureDigital: rePointTotalPureDig(strResult); return TRUE;
    case rPT_Word:        rePointTotalWord(strResult);    return TRUE;
    default:
        return FALSE;
    }
}

const rePtTotalType CartridgeFinder::rePointTotal(CString strResult)
{ 
    int dianIndex(0);
    if ( strResult.Find("點") >= 0)
    {
         dianIndex = strResult.Find("點");
//          中心點;
//          1點;
//          12點;
//          三點;
         CString centerSample = strResult.Mid(dianIndex-6, 6);
         CString oneSample = strResult.Mid(dianIndex-2, 2);
         CString wordSample = strResult.Mid(dianIndex-2, 2);

         if ( (atoi(oneSample) == 1) || (centerSample.Find("中心") >= 0) )
             return rPT_Center;
         else if (atoi(wordSample) == 0)
             return rPT_Word;
         else
             return rPT_Normal;        
    }
    //分開的
    else if (isNumber(strResult))
        return rPT_PureDigital;
    else
    {
        strResult.Empty();
        return rPT_NoMatch;
    }
}

void CartridgeFinder::rePointTotalCenter(CString& strResult)
{ strResult.Format("中心點"); }
void CartridgeFinder::rePointTotalNormal(CString& strResult)
{
    const int colorName(strResult.Find("點"));
    int digitsCount(0);
        if (strResult.Left(colorName).GetLength() > 3)
        {
            //字串在三位數字前有別的字,用轉成數字判斷
            if ( atoi(strResult.Mid(colorName-(3), 3)) >100)
                digitsCount = 3;
            else if ( atoi(strResult.Mid(colorName-(2), 2)) >10)
                digitsCount = 2;
            else if ( atoi(strResult.Mid(colorName-(1), 1)) > 0)
                digitsCount = 1;
        }
        else
            //三位數以內
            digitsCount = strResult.Left(colorName).GetLength();
        
        strResult.Format("%3s點", strResult.Mid(colorName-(digitsCount), digitsCount)  );
}

void CartridgeFinder::rePointTotalWord(CString& strResult)
{
    const int DianIndex(strResult.Find("點"));
    CString Word;
    Word.Format("%s", strResult.Left(DianIndex));

         if ( (DianIndex - Word.Find("一") == 2) && (Word.Find("一") >= 0) ) strResult.Format("中心點");
    else if ( (DianIndex - Word.Find("二") == 2) && (Word.Find("二") >= 0) ) strResult.Format("2點");
    else if ( (DianIndex - Word.Find("三") == 2) && (Word.Find("三") >= 0) ) strResult.Format("3點");
    else if ( (DianIndex - Word.Find("四") == 2) && (Word.Find("四") >= 0) ) strResult.Format("4點");
    else if ( (DianIndex - Word.Find("五") == 2) && (Word.Find("五") >= 0) ) strResult.Format("5點");
    else if ( (DianIndex - Word.Find("六") == 2) && (Word.Find("六") >= 0) ) strResult.Format("6點");
    else if ( (DianIndex - Word.Find("七") == 2) && (Word.Find("七") >= 0) ) strResult.Format("7點");
    else if ( (DianIndex - Word.Find("八") == 2) && (Word.Find("八") >= 0) ) strResult.Format("8點");
    else if ( (DianIndex - Word.Find("九") == 2) && (Word.Find("九") >= 0) ) strResult.Format("9點");
    else if ( (DianIndex - Word.Find("十") == 2) && (Word.Find("十") >= 0) ) strResult.Format("10點");
    else
        strResult.Format("");
}

void CartridgeFinder::rePointTotalPureDig(CString& strResult)
{
    int Digital = atoi(strResult);
    if (Digital == 1)
        strResult.Format("中心點");
    else
        strResult.Format("%3d點", Digital);
}

class//Nucleotide

由於「轉錄」所以以生物學的DNA轉錄RNA的隱喻命名,DNA的元素正是Nucleotide。
在此是DNA各個元素,記載著人描述量測項目的方式。

在此記錄的各種資訊都是給人看的。
量測點、背景顏色、參數串
運算子的內容,也是配合軟體運作而制作的特殊運算。

最特別的就是參數串,以浪費空間式的各別定義每一個記憶體空間的單獨使用
std::vector<int> m_Parameters;  //裝真正的參數,其中一個或者有用到的位址有值,其它裝-1。
CString m_paraStr;  //描述(每一個參數只有一個描述)

Nucleotide.h 

#ifndef NUCLEOTIDE_H
#define NUCLEOTIDE_H

#include <vector>

enum PointTotal  { Pn1 = 1, ..., NoPn = 999 };
enum ColorType   { NoColor = 0, ..., White, Red, Green, Blue, Dark };
enum ParaOfPara  { PA_FEover = 0, PA_FElength, ..., PA_Max };
enum FEtype      { FT_1overN = 0, FT_Ncm };

class Nucleotide
{
//建解構子
public:
    Nucleotide(const ColorType& _C = NoColor, const PointTotal& _P = NoPn, 
               const int& _N1 = -1, 
               const int& _N2 = -1, 
               const int& _N3 = -1 );
    ~Nucleotide();

//該項目共幾點    
private:
    PointTotal  m_MsrPointTotal;
public:
                void SetMsrPointTotal(const PointTotal& pn);
    const PointTotal GetMsrPointTotal()    const;
    const    CString GetStrMsrPointTotal() const;

//背景顏色
private:
    ColorType m_BkColor;
public:
               void SetBackColor(const ColorType& clr);
    const ColorType GetBackColor()    const;
    const   CString GetStrBackColor() const;

//參數串
private:
             CString m_paraStr;
    std::vector<int> m_Parameters;
public:
             void SetPara(const ParaOfPara&, const int&);
    const     int GetPara(const ParaOfPara&) const;
    const CString GetStrPara() const;

//運算子
    const BOOL operator==(const Nucleotide& vNucl) const;
          void operator= (const Nucleotide& vNucl);
    const BOOL equalMsrPtTotal(const Nucleotide& vCar) const;
    const BOOL equalBackColor(const Nucleotide& vCar) const;
    const BOOL equalParameter(const Nucleotide& vCar) const;

    const CString ShowMe() const;
};
#endif

Nucleotide.cpp

檢查參數

檢查vector長度應該是否相同(必須相同),並且檢查每一個的參數是否相同
參數內容,只好一個一個檢查。降低if裡的邏輯複雜度。
利用enum提高for迴圈的可讀性
const BOOL Nucleotide::equalParameter(const Nucleotide& vCar) const
{
    BOOL b(TRUE);
    if (vCar.m_Parameters.size() == m_Parameters.size())
        for ( int i = 0; i < PA_Max; ++i)
            if (vCar.m_Parameters[i] != m_Parameters[i])
                b = FALSE;
    else
        ASSERT(0);

    return b;
}

class//Bullet

程式設計的隱喻,和使用者心智模式的隱喻,不一定要相同。
在此。我們使用槍械打靶的隱喻來設計整個量測機制。

這一個部份是子彈的彈頭。用來在裝載量測的資料用的。

  • 類別裡的i()和o()分別是input和output
    input會輸入float或CString兩種資料型態,利用overload的機制設計
  • output則設計成兩種不同的函數oStr和oFlt,分別取得float和CString。
  • 設計一個「最後更新時間」,紀錄資料寫入時的時間,通常十種光學數據會幾乎同時寫入。
  • 空物件判別則是配合其它資料結構上的運算而設計的,在細部設計時會詳細介紹
  • 內部轉態,float to string和string to float兩種方向的轉換。
  • ShowData系列,提供兩種顯示格式方便程式不同的部份呼叫。

Bullet.h

#ifndef BULLET_H
#define BULLET_H

#include <vector>
#include <iterator>

enum ValueKind{VluK_Lv, VluK_Sx, VluK_Sy, VluK_T, VluK_Duv, 
               VluK_Du, VluK_Dv, VluK_X , VluK_Y, VluK_Z, VluK_Total};

//Bullet 彈頭
class Bullet
{
//建解構子
public:
    Bullet();
    Bullet(const Bullet&);
    ~Bullet();

//資料儲存串
private:
    std::vector<float> m_vfValues;
public:
      const float oFlt(const ValueKind&) const;
    const CString oStr(const ValueKind&) const;
             void i(const ValueKind&, const float&);
             void i(const ValueKind&, const CString&);
//最後更新時間
private:
            CTime m_LastModifyTime;
public:
    const CString GetLastTime() const;

//空物件判別
private:
   BOOL isEmptyObj;
public:
       const BOOL IsEmpty() const;


//float, String轉換
private:
      const float str2flt(CString ) const;
    const CString flt2str(const float& ) const;
//運算子
public:
             void operator= (const Bullet&);
    const CString ShowDataReport() const;
    const CString ShowData() const;
};

#endif

Bullet.cpp

用兩種方式檢查物件是否為空

用迴圈檢查每個值是否為0,比較花時間,卻比較準確。使用一個bool來記錄每個值是否曾經寫入,在檢查是否為空時,速度比較快,卻有不準的風險存在。
在此使用ASSERT配合條件編譯,可以在debug版保留程式正確性,在release時保留程式效率。
const BOOL Bullet::IsEmpty() const
{
#ifdef _DEBUG
    BOOL B(TRUE);
    for (std::vector<float>::const_iterator vitor = m_vfValues.begin();  vitor != m_vfValues.end(); ++vitor)
    {
        if ( *vitor != 0.0 )
            B = FALSE;
    }

    ASSERT(B == isEmptyObj);
#endif
    return isEmptyObj;
}

class//TxtFile

使用C++的STL容器vector,來串起一個.txt檔。
以'\n'來切割vector中的每一個容器元素多長。
(如果全部用一個CString,有時Buffer不足會導致錯誤)
檔案的讀取和儲存,則是使用MFC內建的CStdioFile,它的操作和標準C++處理檔案類別很像。

TxtFile.h

#ifndef TXTFILE_H
#define TXTFILE_H

#include <vector>

typedef std::vector<CString> TxtStrData;

class CTxtFile  
{
    CStdioFile ftxt_Std;
    TxtStrData dtxt_Txt;
public:
    CTxtFile(){};
    virtual ~CTxtFile(){};
    BOOL Open(LPCTSTR, CFileException&);
    BOOL Save(LPCTSTR, CFileException&);

    void Close(){ ftxt_Std.Close(); };

    void iTxtData(TxtStrData& data){ dtxt_Txt = data; mem2file(); };
    void oTxtData(TxtStrData& data){ file2mem(); data = dtxt_Txt; };
    TxtStrData oTxtData(){ file2mem(); return dtxt_Txt; };

private:
    void file2mem();
    void mem2file();
    void errorMsg(CFileException&);
};

#endif

TxtFile.cpp

檔案的讀寫

檔案讀寫,其實操作和C++原本的類別操作沒有兩樣。
只是要特別設計的地方在於「發生錯誤」的處理方式,是不是可以馬上顯示出正確的錯誤訊息。
BOOL CTxtFile::Open(LPCTSTR path, CFileException& fx)
{
    if (PathFileExists(path))
    {
        if (ftxt_Std.Open(path, CFile::modeRead | CFile::typeText, &fx))
            return TRUE;
        else
        { 
            errorMsg(fx);
            ftxt_Std.Close();
            return FALSE;  //失敗
        }
    }
    else
        return FALSE;
}

BOOL CTxtFile::Save(LPCTSTR path, CFileException& fx)
{
    if (ftxt_Std.Open(path, CFile::modeCreate | CFile::modeWrite | CFile::typeText, &fx))
        return TRUE;
    else
    {
        errorMsg(fx);
        ftxt_Std.Close();
        return FALSE;
    }
}

錯誤訊息的顯示

檔案做動作時,會出現的錯誤訊息
void CTxtFile::errorMsg(CFileException& fx)
{
    //例外處理
    TCHAR buf[255];
    fx.GetErrorMessage(buf, 255);
    CString strPrompt;
    strPrompt.Format("CTxtFile說:「%s」", buf);
    AfxMessageBox(strPrompt);
}

資料從硬體到記憶體的動作

資料在記憶體中如何存放,會是檔案讀寫的一個重點。
若格式太特殊,則此類別的再用率降低。反之亦然。
所以,在此就將這個設計規劃成獨立的function,可以再設計,讓source code的再用率做一個折衷。
在此設計的方式,是使用一個vector來儲存一整個純文字檔,以'\n'符號來做vector的每一個元素的分隔。巨觀來說就是純文字檔的每一行,存成每一個vector的元素,而每一個元素就是一個CString(MFC的字串)。每個字串的切割需求,可以用繼承的方式來利用這個類別的function。
void CTxtFile::file2mem()
{
    dtxt_Txt.clear();
    CString strTemp;
    while (ftxt_Std.ReadString(strTemp))
    {
        strTemp.Format(_T("%s\n"), strTemp);
        dtxt_Txt.push_back(strTemp);
    }
}

void CTxtFile::mem2file()
{
    if (!dtxt_Txt.empty())
    {
        for (TxtStrData::iterator it = dtxt_Txt.begin(); it != dtxt_Txt.end(); ++it)
            //AfxMessageBox(*it);
                ftxt_Std.WriteString(*it);
    }
}

class//CColorRef

設計一個顏色處理類別,在MFC中的SDI專案中通常是常用在CView的部份,必要時還有CDialog中。
由於,想在量測介面(CDialog)中,做出相似色、相反色、指定色、灰階(RGB同值)控制,所以將MFC內建的COLORREF和一些相對應的功能做進來。

CColorRef.h

#ifndef CCOLORREF_H
#define CCOLORREF_H

class ColorRef
{
    COLORREF  m_color;
public:
    ColorRef();
    ColorRef(const int&, const int&, const int&);
    ColorRef(const COLORREF& clr);
    ColorRef(const ColorRef& clr);
//彩色
                   void iRGB(const int&, const int&, const int&);
    const      COLORREF oRGB() const;
    const unsigned char R() const;
    const unsigned char G() const;
    const unsigned char B() const;
//變色
    const      COLORREF Shift(int shift = 55) const;
    const      COLORREF Invrt() const;
//灰階
                   void iGray(const int&);
//運算子
public:            void operator= (const ColorRef& clr);
    const          BOOL operator==(const ColorRef& clr) const;

private:           void checkColor(const int& r, const int& g, const int& b) const;
             const BOOL checkInv(const int&) const;
};

#endif

CColorRef.cpp

防禦性設計

當賦值時,要先經過這個函數做檢查,檢查完的值 確定安全,才儲存在物件中的記憶體空間
void ColorRef::checkColor(const int& r, const int& g, const int& b) const
{
    ASSERT(r >= 0);    ASSERT(r < 256);
    ASSERT(g >= 0);    ASSERT(g < 256);
    ASSERT(b >= 0);    ASSERT(b < 256);
}

//應用的一些例子
ColorRef::ColorRef(const int& r, const int& g, const int& b):m_color(r, g, b)
{ checkColor(R(), G(), B()); };

ColorRef::ColorRef(const COLORREF& clrR):m_color(clrR)
{ checkColor(R(), G(), B()); };

ColorRef::ColorRef(const ColorRef& clr):m_color(clr.m_color)
{ checkColor(R(), G(), B()); }

void ColorRef::iRGB(const int& r, const int& g, const int& b)
{
    checkColor(r, g, b);
    m_color = RGB(r, g, b);
}

void ColorRef::iGray(const int& gray)
{
    checkColor(gray, gray, gray);
    m_color = RGB(gray, gray, gray);
}

相反色的設計

顏色相反要RGB個別與255相減。但是,會遇見一個問題,顏色在中間帶,進行相反色運算,會得到相似色。 所以在這一個「中間區段」相反色必須要換另一種算法,顏色的呈現,才會有大的對比(這也是要使用相反色的原意)。 在此的例子是使用130(這個值,可以自訂)。
const COLORREF ColorRef::Invrt() const
{
    const int r = ( checkInv(R()) )?(130 - R()):(255 - R());
    const int g = ( checkInv(G()) )?(130 - G()):(255 - G());
    const int b = ( checkInv(B()) )?(130 - B()):(255 - B());

    return RGB(r, g, b);
}
checkInv是用來檢查,這個值是否在中間帶上。
const BOOL ColorRef::checkInv(const int& subclr) const
{
    const int BandWidth(10);
    const int Limit((256 - BandWidth)/2);
    return (subclr < 255-Limit) && (subclr > Limit);
}

相似色的設計

const COLORREF ColorRef::Shift(int shift) const
{
    const int r = (R() < shift)?(R() + shift):(R() - shift);
    const int g = (G() < shift)?(G() + shift):(G() - shift);
    const int b = (B() < shift)?(B() + shift):(B() - shift);
    
    return RGB(r, g, b);
}

文章分類維度

class design (22) 視覺介面設計 (9) Lv1 class (5) 互動設計 (5) CA-SDK (3) Excel (3) 網誌架構 (2) MFC (1)