亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

MFC中用正則表達式進行有效性驗證

2019-11-17 05:07:31
字體:
來源:轉載
供稿:網友
  正則表達式最實用的一個地方是驗證用戶輸入。它可以輕松驗證郵編、電話號碼、信用卡號碼——以及現實世界中各種類型的信息。一個正則表達式可以替換成打甚至上百行過程代碼。
UNIX 和 Web 編程語言如 Perl從一開始就有正則表達式,但在 Windows 世界或MFC,從來都是使用第三方庫,一直到 .NET 框架才結束這個局面。因此現在 .NET 提供一個完整的正則表達式庫,為什么不在MFC應用程序中使用它呢?利用 RegexWrap 庫,你甚至都不需要托管擴展或 /clr。

   MFC 已經具備一種稱為“對話框數據交換”(Dialog Data Exchange,即 DDX)以及“對話框數據驗證”(Dialog Data Validation,即 DDV)的機制來驗證對話框輸入。從技術上講,DDX 只是在屏幕和你的對話框對象之間傳輸數據,而 DDV 才驗證數據。當你從對話框的 OnOK 處理例程中調用 UpdateData 時 DDX 才開始工作。

// user PRessed OK:
void CMyDialog::OnOK() {
  UpdateData(TRUE); // 獲得對話框數據
  ...
}

   UpdateData 是一個虛擬 CWnd 函數,你可以在自己的對話框中重寫這個函數。其布爾型(Boolean)參數告知是將信息拷貝到屏幕還是相反從屏幕拷貝信息。(你可以在 OnInitDialog 中調用 UpdateData(FALSE)以便初始化對話框)。默認的 CWnd 實現創建一個 CDataExchange 對象并將它傳遞到另一個虛擬函數,DoDataExchange,你得重寫這個函數去調用專門的 DDX 函數來為單獨的數據成員傳遞數據:

void CMyDialog::DoDataExchange(CDataExchange* pDX) {
  CDialog::DoDataExchange(pDX);
  DDX_Text(pDX, IDC_NAME, m_name);
  DDX_Text(pDX, IDC_AGE, m_age);
  ...
  // etc.
}

   這里 IDC_NAME 和 IDC_AGE 是編輯控制的 IDs,m_name 和 m_age 分別是 CString 和 int 數據成員。DDX_Text 將用戶輸入的 Name 和 Age 拷貝到 m_name 和 m_age(用一個重載順便將 Age 轉變成 int)。DDX 函數知道走哪條路,因為當從屏幕拷貝到對話框時,CDataExchange::m_bSaveAndValidate 為 TRUE,反之則為 FALSE。MFC 為各種數據和控制類型加載 DDX 函數。例如,DDX_Text 至少有一些重載函數用來將輸入文本拷貝和轉換成不同的類型,如 CString、int、double、COleCurrency 等等。DDX_Check 用來將復選框的狀態轉換成整型值,DDX_Radio 則對單選按鈕做同樣的事情。

   DDX 函數傳輸數據;DDV 函數則驗證它。例如,為了限制用戶名稱為 35個字符,你可以這樣做:

// in CMyDialog::DoDataExchange
DDX_Text(pDX, IDC_NAME, m_sName); // 獲得/設置值
DDV_MaxChars(pDX, m_sName, 35); // 驗證

   為了限定你的用戶年齡為 1-120之間的一個整數,你可以這樣寫:

// m_age is int
DDX_Text(pDX, IDC_AGE, m_age);
DDV_MinMaxInt(pDX, m_age, 1, 120);

   雖然 DDX 工作表現得很好,DDV 是不免有點老土。MFC 在有效性驗證方面所能做到的很有限。你可以在文本域中限制數字字符,不同類型的最小/最大約束。最小/最大是不錯,但假如你想驗證郵編或電話號碼怎么辦?MFC 對此無能為力。你不得不編寫自己的 DDV 函數。當我第一次用正則表達式實現有效性驗證時,我只要寫一個函數即可,就像這樣:
 
void DDV_Regex(CDataExchange* pDX, CString& val,
LPCTSTR pszRegex)
{
  if (pDX->m_bSaveAndValidate) {
   CMRegex r(pszRegex);
   if (!r.Match(val).SUCcess()) {
    pDX->Fail(); // throws exception
   }
  }
}

   這使你很輕易象下面這樣用正則表達式驗證輸入:

// in CMyDialog::DoDataExchange
DDX_Text(pDX, IDC_Zip, m_zip);
DDV_Regex(pDX, m_zip,_T("^//d{5}(-//d{4})?$"));


   好酷啊,僅用四行代碼就搞掂。(當然,那要假設你有 RegexWrap——否則你得使用托管擴展直接調用框架 Regex 類。)DDV_Regex 在 MFC 的 DDX/DDV 方案中工作表現很完美,但是當我開始添加更多的域時,我馬上發現一些 DDX/DDV 的主要缺點,其一,假如域輸入無效,則每個 DDV 函數都顯示一個出錯消息框并丟出異常,那么要是有五個無效域,用戶就會看到五個消息框——真實糟透了!此外,在對 DDV 的調用中,我不想將正則表達式寫死在代碼中。但我拒絕 DDX/DDV 的主要理由是它太程序化。為了驗證新的域,你不得不添加另外的數據成員以及在 DoDataExchange 加更多的代碼,不久這個函數便膨脹臃腫,就像下面這樣:

DDX_Text(pDX, IDC_FOO,...);
DDV_Mumble(pDX, ...)
DDX_Text(pDX, IDC_BAR,...);
DDV_Bletch(...)
... // etc for 14 lines

   為什么我非得要墨守成規編寫過程指令來描述固有的驗證規則呢?我的五條編程最高準則之一是:拒斥程序化代碼。另一個是:一個表格勝過一千行代碼。你肯定猜到我要干什么了。最終,我編寫自己的對話框驗證系統,一個基于規則的、表格驅動的驗證系統。它依靠在 DDX 的最上層,但廢掉了 DDV 并有好得多的用戶接口。當然,它還易于使用,借助正則表達式進行驗證。所有細節都封裝在一個類中,CRegexForm,你可以在任何 MFC 對話框中使用這個類。

    與往常一樣,我編寫了一個測試程序來示范它的工作原理。初看起來,TestForm 有點像再平常不過的基于 MFC 的對話框程序。它的主對話框中有幾個編輯框——郵編、SSN(社會保險號),電話號碼等。但當你通過對 TestForm 的測試,你會很快熟悉到它蘊含著許多玄機。
假如你用tab鍵在輸入域間移動,TestForm 會顯示一個工具提示,它描述在這個輸入域能輸入什么(如圖 Figure 1)。假如你敲入了一個非法字符——例如,在電話號碼輸入域敲入一個字符——TestForm 將拒絕接受該字符并發出蜂鳴聲。當你按下確認鍵或OK鍵,你會得到一個描述所有無效輸入域的出錯信息框,如圖 Figure 2 所示。所有的錯誤都顯示在一個消息框中,而不是每個錯誤一個消息框。接著當用戶tab到其中一個無效輸入域時,TestForm 會再次在對話框本身的一個應用程序提供的“反饋”窗口內顯示出錯信息(如 Figure 3),因此用戶不必記住錯誤信息所描述的內容,當他們更正每個無效輸入域時,窗體會提醒他們。假如只有一個輸入域無效,TestForm 會放棄消息框而直接在“反饋”窗口顯示錯誤信息。

   所有這些神奇的特性都是由 CRegexForm 自己實現的。你只需使用它即可,使用方法相當直白,首先你得定義一個自己窗體。下面是 TestForm 的窗體內容,這些定義位于 MainDlg.cpp:

// form/field map
BEGIN_REGEX_FORM(MyRegexForm)
RGXFIELD(IDC_ZIP,RGXF_REQUIRED,0)
RGXFIELD(IDC_SSN,0,0)
RGXFIELD(IDC_PHONE,0,0)
RGXFIELD(IDC_TOKEN,0,0)
RGXFIELD(IDC_PRIME,RGXF_CALLBACK,0)
RGXFIELD(IDC_FAVCOL,0,CMRegex::IgnoreCase)
END_REGEX_FORM()

   這個宏定義了一個靜態表格,表格描述每個編輯控制域。大多數情況下你只需要控制 ID,還要有地方放標志和 RegexOptions。例如,在 TestForm 中,郵政編碼是必輸域(RGXF_REQUIRED),質數(Prime Number)輸入域使用回調(稍后會具體討論),最喜愛的專欄作家(IDC_FAVCOL)指定 CMRegex::IgnoreCase,它使得大小寫 不敏感。

   看著表格你可能想知道正則表達式在哪?;卮鹗牵涸谫Y源文件中。對于每個域/控制ID,CRegexForm 都期望有一個具有相同ID的資源串。資源串由五個子串組成,子串之間用新行符(''/n'')分隔。一般格式為:
“Name/nRegex/nLegalChars/nHint/nErrMsg”。以下是 IDC_ZIP 所用的串:

 "Zip Code/n^//d{5}(-//d{4})?$/n[//d-]/n##### or #####-####"
   第一個子串“Zip Code”是域名。第二個“^/d{5}(-/d{4})?$”,是用于驗證郵編的正則表達式。(在資源串中必須敲入兩個反斜線,目的是轉義正則表達式中反斜線)。第三個子串是另外一個正則表達式,用來描述合法字符。對于郵編來說,即是“[/d-]”,意思是答應數字和連字符(hyphen)。假如輸入域無字符限制,你可以通過敲入兩個連續的新行符(“/n/n”意思是空子串)省略 LegalChars 檢查。第四個子串全部為工具提示串。最后你可以提供第五個子串,假如該輸入域無效則顯示錯誤信息。對于郵編來說,它沒有錯誤信息,所以 CRegexForm 產生一個默認的信息,形式為“Should be xxx”,xxx 被工具提示替代?!癝hould be”本身即是另一個資源串(稍后還要說到)。這些子串中,只有第一個域是必輸域。


   為什么用資源串來保存所有信息,而不直接在域映射中編碼處理呢?首先,將它放在映射中使得代碼很笨拙。把這些亂七八糟的字符串放在不顯眼的地方有利于代碼更整潔。此外宏無法處理可選參數,根據你所用參數的多少,你需要多個宏,如:RGXFIELD3、RGXFIELD4 和 RGXFIELD5。這樣不是太笨拙了嘛?使用資源串真正的好處在于輕易本地化。翻譯者可以翻譯字符串并創建不同的資源 DLLs。甚至正則表達式本身都需要翻譯(不同的國家和地域郵編可能是不同的),所以它們也放在資源串中。

   此外,再讓我順便指出用正則表達式解析這些子串是多么輕易。MFC 有一個 26 行的專門用來解析子串的函數 AfxExtractSubString,但 CRegexForm 使用 CMRegex 來做只要一行代碼!

CString str;
str.LoadString(nID);
vector<CString> substrs = CMRegex::Split(str, _T("/n"));

   現在 substrs[i] 是第 i 個子串,假如你想知道有多少個子串,調用 substrs.size()即可。我真的很興奮我包裝了 Split 函數來返回 STL vector。

   一旦你用 BEGIN/END_REGEX_FORM 定義了自己的域映射并編寫了自己的資源串,下一步要做的就是在對話框中實例化 CRegexForm 并進行初始化:

// in OnInitDialog
m_rgxForm.Init(MyRegexForm, this, IDS_MYREGEXFORM, MYWM_RGXFORM_MESSAGE);

   自然,CRegexForm 需要域映射并指向你的對話框;第二和第三個參數是另一個資源串和回調消息ID。與單獨的域字符串一樣,初始串由包含新行符分隔的多個子串組成。對于 TestForm,IDS_MYREGEXFORM 是“Error: %s/nRequired/nShould be: %s/nBad Value”。第一個子串“Error: %s”是錯誤前綴。CRegexForm 用來顯示“Error: xxx,”,xxx 是實際的出錯信息。第二個子串,“Required,”是一個詞/短語,當該域為必輸域(RGXF_REQUIRED)時使用。第三個子串“Should be: %s,”我前面已經描述過,CRegexForm 用它來產生出錯信息?!癝hould be: xxx”中的 xxx 是域提示。最后一個子串,“Bad Value,”CRegexForm 可以用它來放任何信息,當域沒有提示也沒有出錯信息時使用。用戶是看不到此信息的,因為你肯定會為每一個域配一個提示或出錯信息,對不對?   Init 的最后一個參數 MYWM_RGXFORM_MESSAGE 是應用程序定義的回調消息 ID,利用它可以使 CRegexForm 與你的應用程序溝通并做一些需要過程代碼來定制驗證的事情。
假如你需要用數學算法來驗證你的輸入,你可以在域標志中設置 RGXF_CALLBACK,CRegexForm 將在進行驗證時用通知代碼 RGXNM_VALIDATEFIELD 方式向對話框發送回調消息。TestForm 使用回調來驗證其 Prime Number 域;具體具體參見 Figure 4。

Figure 4 Procedural Validation Using Callback Message //////////////////
// Handle notification from Regex Form Manager:
// Do custom validation for Prime Number.
//
LRESULT CMainDlg::OnRgxFormMessage(WPARAM wp, LPARAM lp)
{
  UINT nID = LOWord(wp);
  UINT nCode = HIWORD(wp);
  if (nCode==RGXNM_VALIDATEFIELD) { // custom validation:
   if (nID==IDC_PRIME) {
    const CString& val = *(CString*)lp;
    if (val.IsEmpty())
     return RGXERR_OK;
    int p = _tstoi(val);
    return IsPrime(p) ? RGXERR_OK : RGXERR_NOMATCH;
   }
   ASSERT(FALSE); // shouldn't happen
  }
  return 0;
}

   CRegexForm 用其內部擁有的 CStrings 來進行 DDX,所以你不必為每個文本域定義對話框成員。你只要調用 CRegexForm 來傳遞數據即可。

void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
  CDialog::DoDataExchange(pDX);
  m_rgxForm.DoDataExchange(pDX);
}

   當你初始化 CRegexForm 時,它分配一個 protected 類型的 FLDINFO 結構數組,映射中的每個域都有一個這樣的數組。FLDINFO 結構的成員之一是 FLDINFO::val,類型為 CString,用來保存當前的域值。CRegexForm 在內部使用以此 CString 作為參數的 DDX_Text。你可以通過調用 CRegexForm::GetFieldValue 或 SetFieldValue 獲取或設置該內部域值,它們都用控制ID來區分域。


m_rgxForm.SetFieldValue(IDC_ZIP,_T("10025"));
   CRegexForm 將所有值都當作文本對待,并將它們存儲在 CStrings 中,同時提供 GetFieldValInt 和 GetFieldValDouble 方法來獲得轉換為 int 或 double 的值。對于其它類型,你得自己進行轉換——或者你仍可以用 MFC DoDataExchange 中的 DDX 函數。TestForm 有一個 “Populate”按鈕,它調用 CRegexForm::SetFieldValue 將樣板數據填充到窗體中,如圖 Figure 3 所示。通常,CRegexForm 使用控制 ID 來區分輸入域。它包含有 GetFieldName、GetFieldHint 和 GetFieldError 來獲取域名、提示和出錯信息——它們都帶有一個參數就是控制 ID。

   到此,我說明了如何創建域映射,編寫資源串,初始化你的 CRegexForm 以及通過 DDX 來關聯。所有這些都是序曲。真正的用戶輸入驗證是在用戶按下 OK 鍵后進行的:

void CMyDialog::OnOK()
{
  UpdateData(TRUE); // 拷貝屏幕輸入->對話框
  int nBad = m_rgxForm.Validate();
  if (nBad>0) {
   m_badFields = m_rgxForm.GetBadFields();
   ...
}

   UpdateData 調用 MFC 的 DDX 機制,即調用對話框的 DoDataExchange。然后 DoDataExchange 調用 CRegexForm::DoDataExchange,從而將用戶輸入拷貝到其內部的 FLDINFO 結構。接著 CRegexForm::Validate 遍歷輸入域,針對域的正則表達式調用 CMRegex::Match 來驗證每一個域。假如域輸入無效,CRegexForm 便在其內部的 FLDINFO 中設置錯誤碼 RGXERR_NOMATCH 或者必輸于域假如為空,則設置 RGXERR_MISSING。Validate 返回無效域數量。假如有無效域,你可調用 CRegexForm::GetBadFields 來獲得一個無效域 IDs 數組(STL vector)。然后你可以遍歷該數組以獲取各個錯誤嗎和出錯信息。這便是 TestForm 中 CMainDlg 建立其錯誤消息框所做的事情,如 Figure 2 所示。假如只有一個域無效,CMainDlg 調用 CRegexForm::ShowBadField 高亮該輸入域并在反饋窗口顯示出錯信息,如圖 Figure 3 所示。假如所有域都沒問題,TestForm 便顯示一個消息框展示輸入的值。


  在實際應用中,你會將這些值拷貝到其最終目的地。Figure 6 是 CMainDlg::OnOK 的全部代碼。通過數據交換中的非耦合式的數據驗證,CRegexForm 給予你更充分的 UI 控制,并使你避免 MFC 固有的缺陷。

Figure 6 CMainDlg::OnOK //////////////////
// User pressed OK: validate form and display results: error message
// or field values—but don't call base class to end dialog.
//
void CMainDlg::OnOK()
{
  UpdateData(TRUE); // get dialog data int nBad = m_rgxForm.Validate(); // validate
  CString msg;
  if (nBad>0) {
   vector<UINT> badFields = m_rgxForm.GetBadFields();
   BOOL beep = TRUE;
   if (nBad>1) {
    // Multiple bad fields: show message box with bad fields.
    msg = _T("The following fields are bad:/n/n");
    vector<UINT>::iterator it;
    for (it = badFields.begin(); it!=badFields.end(); it++) {
     UINT nID = *it;
     CString s;
     s.Format(_T("%s: %s/n"), m_rgxForm.GetFieldName(nID),m_rgxForm.GetFieldErrorMsg(nID));
     msg += s;
    }
    MessageBox(msg,_T("Oops—Some fields are bad."),MB_ICONEXCLAMATION);
    beep = FALSE; // message box already beeped; don't beep again
   }
   // to highlight first bad field whether one or many
   UINT nID = badFields[0];
   m_rgxForm.ShowBadField(nID, beep, TRUE); } else {

   // all fields OK: show feeback
   msg = _T("You Entered:/n/n");
   for (int i=0; MyRegexForm[i].id; i++) {
    CString name = m_rgxForm.GetFieldName(MyRegexForm[i].id);
    CString val = m_rgxForm.GetFieldValue(MyRegexForm[i].id);
    if (val.IsEmpty())
     val = _T("(nothing)");
     CString temp;
     temp.Format(_T("%s = %s/n"), name, val);
     msg += temp;
   }
   MessageBox(msg,_T("Congratulations! All fields OK."),MB_OK);
   m_rgxForm.Feedback(_T(" All fields OK or empty!"));
  }
}

   我提到的反饋窗口,它由 CRegexForm 全權治理;你只需通過調用 CRegexForm::SetFeedBackWindow 提供一個這樣的窗口即可。你可以為出錯信息指定一種顏色。CRegexForm 還負責提示功能。默認情況下,當用戶tab到某個新輸入域時顯示域提示(如圖 Figure 1)。你可以調用 CRegexForm::SetShowHints(FALSE)來關閉提示功能。SetShowHints(TRUE, nDelay, nTimeout) 可以再次打開提示功能,這里 nDelay 是顯示提示之前要等待的毫秒數(默認值=250),nTimeout 是提示顯示的毫秒數(默認值=0,表示一直顯示)。當用戶離開輸入域時(EN_KILLFOCUS),CRegexForm 自動清除其提示。TestForm 使用 SetShowHints 實現復選框提示的開啟和關閉(參見 Figure 1)。

   還有另外一個特性是否提出來我有些舉棋不定。CRegexForm 有一個選項來做立即驗證。我不建議使用它,因為我覺得那是一種不好的GUI設計,但很多時候在沒有輸入有效的信息之前你不想讓用戶tab到下一個輸入域。在這種情況下,你可以在域映射中使用 RGXF_IMMED,或調用 SetValidateImmed 來使所有域都進行立即驗證。TestForm 有一個復選框專門控制立即驗證功能的開關。假如選中,你會看到為什么立即驗證不是個好主意。

   最后,看看最小/最大和字符限制約束特性?最小/最大是正則表達式無法做到的一件事。雖然“.{0,35}”是一個描述最多35個字符長的字符串正則表達式,但你還是愿意使用 EM_LIMITTEXT 來限制編輯控制的文本長度,所以當用戶敲入過長的字符時發出蜂鳴聲。因為我容忍實現了一個窗體驗證系統,但它缺乏 MFC 已經支持的特性,我引進了“偽正則表達式”的概念。例如,IDC_AGE 的正則表達式(Age 輸入域)為“rgx:minmax:int:1:120,maxchars:3”。顯然,它不是一個真正的正則表達式。它是一個偽表達式,它由 CRegexForm 自己來識別和解釋。其一般格式是:“rgx:eXPr,expr,..expr”,這里每個表達式描述不同的約束。目前支持兩種類型:“minmax:type:minval:maxval”(此處 type 是 int 或 double)以及“maxchars:maxval”。CRegexForm 負責做專門解析,并對 maxchars 使用 EM_LIMITTEXT,就像 DDV_MinMaxInt 一樣。具體實現細節請下載源代碼。至于資源子串,正則表達式解析它們簡直是小菜一碟。

   CRegexForm 所能做的事情我已經說完了。它是如何工作的呢?由于篇幅所限,恕不贅言,但我大概勾勒一下。CRegexForm 我的 CSubclassWnd 來子類化對話框。對之前不了解它的讀者來說,CSubclassWnd 是我很早以前寫的一個類,它使用Windows的子類化機制來截獲發送到另一個窗口的消息。CSubclassWnd 真正酷的地方是它不需要在你的類層次中插入新類便能讓你子類某個 MFC 窗口。我可以從 CDialog 派生出 CRegexForm,但那樣你就得從 CRegexForm 派生自己的對話框。那么假如你已經自己實現了從 CDialog 派生的 CBetterDialog 怎么辦呢?此時,你不得不進行外科手術來插入 CRegexForm,結果將不可預見。

   這是MFC的一大缺陷:它用派生實現子類化,所以類層次確切地反映了 Windows 子類機制。但沒必要這樣,編寫像 CRegexForm 這樣的插件類來子類化你的對話框而不用改動你的類層次是更好的解決方法。對我來說,CSubclassWnd 是如此不可或缺,沒有它我無法編程!

   CRegexForm 使用 CSubclassWnd 截獲 EN_KILLFOCUS 和 EN_SETFOCUS 以隱藏和顯示提示,截獲 EN_CHANGE 以便用戶敲入任何信息后清除輸入域的出錯狀態。CRegexForm 用我的 CPopupText 類實現本身的提示,CPopupText 類第一次討論是在 2000年九月的專欄。為了防止用戶敲入不答應的字符,CRegexForm 為每一個具有 LegalChars 正則表達式的編輯控制安裝了另一個 CSubclassWnd 派生的鉤子。這是一個嵌套類,CRegexForm::CEditHook 截獲發送到編輯控制的 WM_CHAR 消息,它吃掉任何非法輸入字符并發出蜂鳴。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久99久国产精品黄毛片入口| 国产精品久久婷婷六月丁香| 69久久夜色精品国产7777| 日韩欧美一区二区在线| 亚洲色图在线观看| 亚洲精品永久免费精品| 日韩激情av在线播放| 国内成人精品一区| 成人精品视频在线| 久久99热这里只有精品国产| 超碰97人人做人人爱少妇| 国产精品羞羞答答| 在线观看日韩www视频免费| 久久久999国产精品| 亚洲欧美在线一区| 孩xxxx性bbbb欧美| 成人久久久久久久| 国产精品久久激情| 久久久久久久一区二区| 69久久夜色精品国产69| 成人性生交大片免费观看嘿嘿视频| 精品国产精品三级精品av网址| 成人a在线观看| 国产精品一区二区久久久| 欧美资源在线观看| 欧美精品九九久久| 日韩女优人人人人射在线视频| 一区二区国产精品视频| 亚洲欧洲免费视频| 午夜精品久久久久久久99热浪潮| 色综合视频网站| 26uuu国产精品视频| 国产精品男人爽免费视频1| 久久久久久久爱| 日韩黄色高清视频| 亚洲性生活视频在线观看| 欧美疯狂性受xxxxx另类| 亚洲天堂成人在线| 黄色精品在线看| 色老头一区二区三区| 国内精品中文字幕| 精品呦交小u女在线| 久久艳片www.17c.com| 日韩av在线一区| 色哟哟亚洲精品一区二区| www欧美xxxx| 日韩在线观看免费网站| 黄色精品在线看| 欧美性猛交丰臀xxxxx网站| 热久久免费国产视频| 国产精品丝袜白浆摸在线| 国产一区二区动漫| 亚洲天堂av在线播放| 性金发美女69hd大尺寸| 91美女片黄在线观| 久久69精品久久久久久国产越南| 亚洲日本成人网| 亚洲精品国产精品自产a区红杏吧| 成人黄色影片在线| 国产精品91在线观看| 国产精品老女人精品视频| 97色在线播放视频| 久久精品中文字幕一区| 青草热久免费精品视频| 亚洲精品短视频| 久久久久国产精品免费| 久久激情五月丁香伊人| 伊人久久男人天堂| 欧美性videos高清精品| 蜜臀久久99精品久久久无需会员| 国产精品嫩草视频| 国产免费一区二区三区香蕉精| 亚洲精品电影久久久| 久久免费精品日本久久中文字幕| 国产精品精品一区二区三区午夜版| 中文字幕日韩在线视频| 久久久久久久久久久成人| 欧美裸体xxxx| 成人激情综合网| 欧美视频精品一区| 欧美第一淫aaasss性| 欧美专区日韩视频| 亚洲精品久久久久中文字幕二区| 亚洲第一区中文字幕| 亚洲欧美日韩另类| 国内精品久久影院| 国内精品模特av私拍在线观看| 亚洲欧美在线免费观看| 中日韩美女免费视频网站在线观看| 成人免费视频97| 国产成人av网| 亚洲精品视频在线播放| 亚洲美女性生活视频| 国产精品久久激情| 亚洲一区二区自拍| 中文字幕亚洲无线码a| 亚洲午夜女主播在线直播| 欧美日韩亚洲视频| 国产一区二区三区在线观看视频| 国产91免费看片| 最新亚洲国产精品| 日韩在线激情视频| 久久天天躁狠狠躁夜夜av| 日韩视频在线免费| 97不卡在线视频| 国产91精品久久久久久久| 夜夜嗨av色综合久久久综合网| 日韩免费在线观看视频| 欧美色欧美亚洲高清在线视频| 亚洲免费视频一区二区| 久久天天躁夜夜躁狠狠躁2022| 欧美激情精品久久久久久大尺度| 中文字幕日韩av电影| 97婷婷涩涩精品一区| 久热精品在线视频| 久久69精品久久久久久久电影好| 国产精品成人av在线| 欧美激情成人在线视频| 日韩av电影国产| 国产精品视频网址| 欧美大片欧美激情性色a∨久久| 欧美香蕉大胸在线视频观看| 国产精品爽爽爽爽爽爽在线观看| 国产精品十八以下禁看| 国产精品18久久久久久麻辣| 亚洲tv在线观看| 亚洲最大的免费| 正在播放国产一区| 亚洲人成网站色ww在线| 91爱视频在线| 欧美激情成人在线视频| 91av视频在线免费观看| 精品久久久久久久久久久久久久| 国产精品91在线观看| www国产91| 久精品免费视频| 国产精品久久久久久久午夜| 日韩中文在线中文网在线观看| 国产欧美精品日韩| 国产男人精品视频| 久久中文字幕一区| 狠狠色噜噜狠狠狠狠97| 亚洲国产精品久久久久| 91欧美精品成人综合在线观看| 精品国产乱码久久久久久虫虫漫画| 国产精品美乳一区二区免费| 欧洲日本亚洲国产区| 国产成人精品综合| 久久99热这里只有精品国产| 国产成人亚洲综合青青| 亚洲深夜福利在线| 国产精品久久婷婷六月丁香| 国产精品视频xxxx| 日本免费久久高清视频| 性亚洲最疯狂xxxx高清| 国产欧美日韩视频| 亚洲成人a**站| 97人人模人人爽人人喊中文字| 欧美老少配视频| 国产精品直播网红| 黑人与娇小精品av专区| 精品久久久久久久久久ntr影视| 精品久久久久久久久久久久| 欧美高清视频一区二区|