iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 4
0
Software Development

當我遊走在程式的初學路上-從入門到放棄系列 第 4

Project 1 - 自我介紹程式(4):活用「語法、邏輯、資料結構、型態函式庫」-確保正確的資料進到程式當中

  • 分享至 

  • xImage
  •  

Project 1 - 自我介紹程式(3):看型別用Bug教工程師做人,它會決定程式的動盪與安逸
https://ithelp.ithome.com.tw/articles/10213621

複習第三天:回顧型別在程式的那些地方帶來影響

  1. 從檔案、輸入欄位得得資料的動作 => 型別不對、NULL、空白字串、宣告的變數範圍過小
  2. 開始處理程式的過程中 => 加減乘除、文字分析與加工
  3. 處理完資料後的收尾 => 電腦資源的釋放、輸出存放端或呈現資料端所接受的資料格式。

第四天簡介:確保正確的資料進到程式

延續昨天在型別的介紹與實例展示,今天我們來思考,該如何透過程式碼,從哪些方向撰寫程式碼,處理型別可能帶來的程式錯誤,這個過程就是所謂的「卡控」或「校驗」。

從教學者與學習者看放棄程式的人

對於一個大型的資訊系統,包括金融系統、商業系統、校務系統、工程系統,每天必須長時間不間段的處理大量資料,如果在資料的接收過程中產生的程式錯誤,將會接連造成後續的資料錯誤,最後的資料流失或資料錯誤,對使用者產生的損失和影響大到難以估計。

然而要做好資料的卡控和校驗,需要對「語法、邏輯、資料結構、型態函式庫」的四個觀念和「資料本身」有一定的了解和敏銳度。除了演算法和資料結構解題的練習之外,還需要有專案開發和維護系統,從面對真實的功能需求當中學習。

面對型別在昨天製造的BUG

情況一:確保所有欄位都有輸入

每次做任何處理資料前,最一開始要檢查的有沒有資料,避免NULL和空字串造成程式錯誤。


//檢查是否所有欄位都有被輸入
TextBox[] allTextBox = new TextBox[] { nameTextBox, homeTownTextBox, 
                                       birthdate_YearBox, birthdate_MonthBox, 
                                       birthdate_DayBox };

string[] allTextBoxName = new string[]{"姓名", "家鄉", "出生年", "出生月", "出生日" }; // 也可以放在TextBox的Tag屬性

StringBuilder errorMsg = new StringBuilder();

for (int i = 0; i < allTextBox.GetLength(0); i++)
{
    if (allTextBox[i].Text == null || allTextBox[i].Text == "")
    {
        errorMsg.AppendLine(string.Format(@"請輸入 「{0}」", allTextBoxName[i]));
    }
}

if (errorMsg.ToString() != "")
{
    MessageBox.Show(errorMsg.ToString());
    return;
}

有沒有輸入文字的條件判斷,在C#可使用 string 函式庫 的 IsNullOrEmpty 函式

if(string.IsNullOrEmpty(allTextBox[i].Text))
{
    errorMsg.AppendLine(string.Format(@"請輸入 「{0}」", allTextBoxName[i]));
}

情況二:如果我的生日輸入非常非常大呢? + 情況三:如果我的生日的年月日輸入英文字呢?

我們經常需要判斷的狀況有「是不是數值」、「英文字母」,生日的年月日都只能是數字

解決方向1: 從源頭解決,替換UI欄位
把生日旁邊三個可以輸入任何文字的TextBox,改換成C#的DateTimePicker,讓使用者只能以日期的形式輸入資料
https://ithelp.ithome.com.tw/upload/images/20190905/20120331Z5uY3yh8vY.png

在程式碼透過DateTimePicker的Value屬性當中的Year、Month、Day,得到數字型態的日期資料
https://ithelp.ithome.com.tw/upload/images/20190905/20120331kVN98J6cmG.png

int birthDate_Year = birthDate_Picker.Value.Year;
int birthDate_Month = birthDate_Picker.Value.Month;
int birthDate_Day = birthDate_Picker.Value.Day;

解決方向2: 撰寫鍵盤事件,讓使用者只能按下數字鍵或控制類按鍵(BackSpace)
使用者如果只能輸入數字,使用Convert.ToInt32或int.Parse就可以確保不會出現問題
我們可以根據使用者按下鍵盤的按鍵字元,判斷輸入的文字是不是數字

char (字元型態) 只能有「一個文字」的變數,string (字串) 可以有「一個或一個以上」的文字。

https://ithelp.ithome.com.tw/upload/images/20190905/20120331ivlPrMacG4.png
https://ithelp.ithome.com.tw/upload/images/20190905/20120331OyALQ30MST.png

private void birthdate_YearBox_KeyPress(object sender, KeyPressEventArgs e)
{
    //取得使用者每次輸入鍵盤當下的文字
    char inputChar = e.KeyChar;

    // 使用者只能輸入數字(char.IsDigit(inputChar))、控制類按鍵(char.IsControl(e.KeyChar))
    // 在keyPress 設定e.Handled = true,文字就無法輸入進去
    e.Handled = ! (char.IsDigit(inputChar) || char.IsControl(e.KeyChar));
}

解決方向3: 在存到變數前檢查
只要在存到變數之前,發現三個輸入欄位的文字不是數字,就不要繼續往下執行
避免輸入的文字經過int.Parse()或Convrt.ToInt32 處理時發生問題

【自行撰寫判斷函式】
你可以寫成一個函式方法 stringIsInt,透過參數num帶入要檢查的字串變數,如果可以成功轉換成 int (try),最後回傳 true,如果轉換出現錯誤(Catch),不是 int 最後回傳 false
https://ithelp.ithome.com.tw/upload/images/20190905/20120331vMDWzBAwRj.png

private bool stringIsInt(string num)
{
    try
    {
        int tryParse = int.Parse(num);
        return true;
    }
    catch (Exception)
    {
        return false;
    }
}

經過簡化後,等同下方的程式碼
https://ithelp.ithome.com.tw/upload/images/20190905/201203313Ut5GheL8W.png

private bool stringIsInt(string num)
{
    int tryParse;

    return int.TryParse(num, out tryParse);
}

在按下自我介紹的Click程式碼,放到 if 的條件當中,檢查生日年月日輸入的文字

https://ithelp.ithome.com.tw/upload/images/20190905/20120331ca5xMeuZ0z.png

if (!stringIsInt(birthdate_YearBox.Text) || !stringIsInt(birthdate_MonthBox.Text) ||  !stringIsInt(birthdate_DayBox.Text))
{
    MessageBox.Show("出生年月日請輸入數字");
    return;
}

【正規表達式】
正規表達式可以做到非常複雜的格式判斷,包括電話號碼、email、URL網址、IP
可以參考下方兩個網址,深入了解正規表達式
https://dotblogs.com.tw/kinanson/2013/05/23/104772

https://ithelp.ithome.com.tw/upload/images/20190905/20120331MxxzdRKPvb.png

透過Regex.IsMatch函式方法判斷,如果符合格式會回傳True。
並搭配 if 校驗和卡控

// Regex.IsMatch(要判斷的資料,判斷是不是符合這個格式)
if (!Regex.IsMatch(birthdate_YearBox.Text, @"\d") || 
    !Regex.IsMatch(birthdate_MonthBox.Text, @"\d") || 
    !Regex.IsMatch(birthdate_DayBox.Text, @"\d"))
{
    MessageBox.Show("出生年月日請輸入數字");
    return;
}

情況四:如果我拿昨天選擇相片的程式碼,用Excel當作大頭貼會發生什麼事情?

1.檢查你有沒有選擇圖片,若沒有選擇照片,得到的照片資料會是NULL,出現錯誤
2.檢查選完照片是不是按下確定,如果按下取消,得到的照片資料會是NULL,出現錯誤
3.檢查你的檔案格式是不是符合常見的圖片格式,如果不符合,無法轉換

https://ithelp.ithome.com.tw/upload/images/20190905/20120331MOrquI7eiP.png

private void photoBox_DoubleClick(object sender, EventArgs e)
{
    OpenFileDialog fileDialog = new OpenFileDialog();
    fileDialog.Filter = "Image files (*.jpg, *.jpeg, *.jpe, *.jfif, *.png) | *.jpg; *.jpeg; *.jpe; *.jfif; *.png";

    if (fileDialog.ShowDialog() == DialogResult.OK && fileDialog.FileName != "")
    {
        photoBox.Image = Image.FromFile(fileDialog.FileName);
    }
}

https://ithelp.ithome.com.tw/upload/images/20190905/20120331TS4Lm61SxB.png

往明天邁進 OR 放棄? 練習完成判斷輸入的生日範圍不合理

1.出生年、月、日不能超過今天,例如今天是2019/09/05,結果生日輸入2019/12/10
2.生日年、月、日不能是負數
3.月份是1~12月,日期根據輸入的月份判斷是否在1~最大天數

只要其中一個不吻合,回傳False

private bool isValidBirthDate(string year, string month, string day)
{
    /*
     撰寫這支副程式的邏輯,根據傳入的年月日判斷是不是有效的生日日期
     例如: isValidBirthDate("2018", "02", "30") => false, 因為2018年沒有2月30日
           isValidBirthDate("2018", "01", "31") => true
           isValidBirthDate("201D", "10", "10") => false, "傳入的年不是純正整數"
           isValidBirthDate("2019", "12", "10") => false, "生日超過今天的日期 (今天是2019/9/05)"
     */

    // 有效的生日日期,最後回傳true
    return true;
}

你會發現看似直覺簡單的卡控和校驗判斷,當中參雜許多陌生的觀念和語法。所以如果你是一個初學者來看這篇,多多少少會感覺到排斥和壓力。慢慢的練習解題、寫Side Project、接觸開發專案(開源或工作),相信能在放棄程式之前的某一天,對資料的敏感度越來越強。


上一篇
Project 1 - 自我介紹程式(3):看型別用Bug教工程師做人,它會決定程式的動盪與安逸
下一篇
Project 1 - 自我介紹程式(5):衍伸變化並複習前幾天的內容 - 實作多語系與跨視窗應用
系列文
當我遊走在程式的初學路上-從入門到放棄9
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言