上色

Code Block

2016年6月3日 星期五

[.Net] 讀取CSV出現亂碼

當CSV檔中含有中文時,使用StremReader讀取通常都會出現亂碼,這是因為.Net預設使用Unicode編碼,但通常Excel還是使用Big5編碼,此時只要以下列方式指定系統編碼即可解決;
System.IO.FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
System.IO.StreamReader sr = new System.IO.StreamReader(fs, System.Text.Encoding.Default);
使用System.Text.Encoding.Default指定系統預設編碼為StremReader的編碼格式。

[.Net] 字串轉為DateTime

通常需要將字串轉為DateTime的時候,一大問題就是來源格式常常不是固定的格式,而是有各種五花八門的格式,這時候就可以利用以下方式進行轉換:

首先定義可能出現的各種日期格式,可參照https://msdn.microsoft.com/en-us/library/8kb3ddd4(VS.71).aspx

string[] dateTimeList = { 
 "yyyy/M/d tt hh:mm:ss", 
 "yyyy/MM/dd tt hh:mm:ss", 
 "yyyy/MM/dd HH:mm:ss", 
 "yyyy/M/d HH:mm:ss", 
 "yyyy/M/d", 
 "yyyy/MM/dd",
 "M/d HH:mm:ss"
}; 

接著就可以開始處理字串,使用CultureInfo.InvariantCulture解析不同國家語言格式的字串(或是套用目前的國家地區設定解析,CultureInfo.CurrentCulture),使用DateTimeStyles.AllowWhiteSpaces避免字串中可能出現的無意義空白;此外為了避免依然轉換錯誤,可以將整段敘述加入到Try...Catch區塊中,再於Catch區塊中對轉換錯誤加以處理。

string DateTime date;
try
{
 date = DateTime.ParseExact(dateString, dateTimeList, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces);
 transferFlag = true;
}
catch (Exception e)
{
 throw new Exception("日期格式錯誤: " + e.Message);
}

Ref: https://dotblogs.com.tw/chhuang/2008/03/18/1921

2016年2月1日 星期一

[Android] Android 6.0 (marshmallow) run-time permission 執行期間權限取得

自Android 6.0開始,除了必須在AndroidManifest.xml中宣告權限,在安裝時告知使用者外,執行期間需要用到特殊權限的話也必須告知使用者並取得。

權限可區分為以下兩大類

  1. 一般權限(PROTECTION_NORMAL),亦即在AndroidManifest.xml中宣告即可,在程式安裝時即授予程式該權限,用戶也無法手動取消。在Android 6.0(API 23)中,一般權限列表如下︰
    • ACCESS_LOCATION_EXTRA_COMMANDS
    • ACCESS_NETWORK_STATE
    • ACCESS_NOTIFICATION_POLICY
    • ACCESS_WIFI_STATE
    • BLUETOOTH
    • BLUETOOTH_ADMIN
    • BROADCAST_STICKY
    • CHANGE_NETWORK_STATE
    • CHANGE_WIFI_MULTICAST_STATE
    • CHANGE_WIFI_STATE
    • DISABLE_KEYGUARD
    • EXPAND_STATUS_BAR
    • GET_PACKAGE_SIZE
    • INTERNET
    • KILL_BACKGROUND_PROCESSES
    • MODIFY_AUDIO_SETTINGS
    • NFC
    • READ_SYNC_SETTINGS
    • READ_SYNC_STATS
    • RECEIVE_BOOT_COMPLETED
    • REORDER_TASKS
    • REQUEST_INSTALL_PACKAGES
    • SET_TIME_ZONE
    • SET_WALLPAPER
    • SET_WALLPAPER_HINTS
    • TRANSMIT_IR
    • USE_FINGERPRINT
    • VIBRATE
    • WAKE_LOCK
    • WRITE_SYNC_SETTINGS
    • SET_ALARM
    • INSTALL_SHORTCUT
    • UNINSTALL_SHORTCUT
  2. 需要執行期間授權的權限,用戶也可以在設定中隨時關閉他們,Android將之分為數個類別,當取得類別中的其中一個權限時,也就等於取得該類別所有權限,分類如下︰

若要取得權限,需在程式碼中加入
ActivityCompat.requestPermissions(Activity activity, String[] permissions, int requestCode)

若要確認是否具有權限,可使用
ContextCompat.checkSelfPermission (Context context, String permission)

而當使用者拒絕授予此權限時,開發者可能會需要說明該權限的用途並再次詢問,這時可使用
ActivityCompat.shouldShowRequestPermissionRationale(Activity activity, String permission)
當使用者拒絕授予權限時,下次調用此函式就會返回true,開發者可在此時加入權限說明。

當使用者選擇授予或拒絕此權限時,會呼叫onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)方法,此時override此方法即可處理需要權限的操作。


具體程式碼如下(以讀寫SD卡為例)︰
    

private final int REQUEST_CODE_ASK_PERMISSIONS = 10;

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
 switch (requestCode) {
  case REQUEST_CODE_ASK_PERMISSIONS:
   if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    // Permission Granted
    //執行需要讀寫SD卡的操作
   } else {
    // Permission Denied
    Toast.makeText(MainActivity.this, getString(R.string.noPermission), Toast.LENGTH_LONG).show();
   }
   break;
  default:
   super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 }
}

private void checkPermission(){
 //讀取關於讀寫外部儲存裝置的權限
 int hasReadExternalStoragePermission = ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
 //若沒有權限則詢問取得
 if (hasReadExternalStoragePermission!= PackageManager.PERMISSION_GRANTED)
 {
  if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE)){
   //當被使用者拒絕後再次詢問時,說明該權限用途,用戶可選擇重試(重新詢問是否授予)或取消(維持不授予權限)
   new AlertDialog.Builder(MainActivity.this)
     .setMessage("該權限用以讀寫SD卡")
     .setPositiveButton("重試", new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int which) {
       ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_ASK_PERMISSIONS);
      }
     })
     .setNegativeButton("取消", null)
     .create()
     .show();
   return;
  }
  ActivityCompat.requestPermissions(MainActivity.this,new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},REQUEST_CODE_ASK_PERMISSIONS);
 }
 else
  //已有權限,直接執行需要讀寫SD卡的操作
}

Reference: Android developer documents