顯示具有 Java 標籤的文章。 顯示所有文章
顯示具有 Java 標籤的文章。 顯示所有文章

2018年8月29日 星期三

Java zip 解壓編碼問題


前幾天有人使用Java zip解壓縮時遇到一個錯誤

java.lang.IllegalArgumentException: MALFORMED

我在幫忙google查詢問題的時候發現到他其實是因為壓縮檔裡面有中文

解壓縮的時候預設用UTF-8出錯
但是提出解法的大多是簡體中文的使用者
所以會看到以下的程式碼

Charset gbk = Charset.forName("GBK");
ZipFile zipFile = new  ZipFile(zipFileName, gbk);


事實上GBK是簡中的編碼所以用GBK去解壓還是會造成亂碼的問題
台灣BIG5的編碼你需要使用的Charset是ms950

Charset gbk = Charset.forName("ms950");
ZipFile zipFile = new  ZipFile(zipFileName, gbk);

將這個紀錄在google blog跟痞客邦上,希望有繁中的使用者遇到這個問題的時候不會找不到正確的編碼而卡住

2015年10月30日 星期五

[Java]複製陣列的方法(System.arraycopy)

有時候需要建立一個新陣列,這個新陣列跟舊的陣列前面都一樣
只有最後幾個值不同或是加了幾個值

或是有兩個陣列,我們需要合併這兩個陣列的時候
除了用for迴圈把陣列一個一個倒進去以外
我們可以使用System的arraycopy方法

文件方法如下
arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

第一個src要放入的是被複製的陣列
srcPos是指定被複雜的陣列從第幾項開始複製

dest放入的是要複製的陣列
destPos是指定要複製的陣列從第幾項開始寫入

length放入的是你總共要複製幾項資料
以下是最常用的兩個範例


public class copyArrayDemo {



 public static void main(String[] args) {

  System.out.println("This is demo 1");

  int[] arr1 = {1,2,3};

  int[] arr2 = new int[arr1.length+1];

  System.arraycopy(arr1, 0, arr2, 0, arr1.length);

  arr2[arr1.length]= 4;

  // arr2 == {1,2,3,4}

  for(int item:arr2){

   System.out.println(item);

  }

  System.out.println("This is demo 2");



  

  String[] array1 = {"item1","item2","item3"};  

  String[] array2 = {"demo1","demo2","demo3"};

  

  

  String[] sum = new String[array1.length+array2.length];

  System.arraycopy(array1, 0, sum, 0, array1.length);

  System.arraycopy(array2, 0, sum, array1.length, array2.length);

  // sum =={"item1","item2","item3","demo1","demo2","demo3"}

  for(String item:sum){

   System.out.println(item);

  }    

 }

}

2015年10月26日 星期一

[Java]StringTokenizer

今天介紹一個除了split()方法以外,分割String的方法
StringTokenizer的方法主要有下列幾個

countTokens():可以知道你的String被Tokenizer分成幾段
hasMoreTokens():檢查StringTokenizer是否還有Token
nextToken():將StringTokenizer的下一個Token用String表示

以下是簡單的範例:



import java.util.StringTokenizer;



public class StringDemo {


 public static void main(String[] args) {

  String demo = "String,int,long,double";



  StringTokenizer st = new StringTokenizer(demo,",");

 

  System.out.println("st has "+st.countTokens()+"tokens");

 

  while(st.hasMoreTokens()){

   System.out.println();

  }

 }

}

2015年10月15日 星期四

[Java面試考題]Map處理

今天去松凌科技面試時遇到的考題
限時30分鐘
做出來後我問了一下面試官說能不能把考題公佈
面試官很慷慨的答應了,表示說他們也希望大家都能夠會處理map
於是我回家後馬上將這題題目重現
中文的註解可能有些誤差,以下是題目跟參考解答
題目詳細內容請見註解




import java.util.HashMap;
import java.util.Map;

public class RightLeft {

Map<String, Integer> left;
Map<String, Integer> right;

public void setUp(){
left = new HashMap<String, Integer>();
left.put("a", 1);
left.put("b", 2);
left.put("c", 3);

right = new HashMap<String, Integer>();
right.put("b", 2);
right.put("c", 4);
right.put("d", 5);

}

/*
* <pre>
* 備住:有兩個Map left right,請在Test()內完成程式碼輸出以下內容
*
* 1.key一樣value不一樣的內容
* 2.key一樣value一樣的內容
* 3.key只存在left不存在right的內容
* 4.key只存在right不存在left的內容
*
*/

public void Test(){

//answer of 1
System.out.println("1.");
for(Object key:left.keySet()){
if(right.get(key)!=null){
if(!right.get(key).equals(left.get(key))){
System.out.println("left key="+key+", value="+left.get(key));
System.out.println("right key="+key+", value="+right.get(key));
}
}
}

//answer of 2
System.out.println("2.");
for(Object key:left.keySet()){
if(right.get(key)!=null){
if(right.get(key).equals(left.get(key))){
System.out.println("left: key="+key+", value="+left.get(key));
System.out.println("right: key="+key+", value="+right.get(key));
}
}
}

//answer of 3
System.out.println("3.");
for(Object key:left.keySet()){
if(right.get(key)==null){
System.out.println("left: key="+key+", value="+left.get(key));
}
}

//answer of 4
System.out.println("4.");
for(Object key:right.keySet()){
if(left.get(key)==null){

System.out.println("right: key="+key+", value="+right.get(key));

}
}


}



public static void main(String[] args) {
RightLeft demo = new RightLeft();
demo.setUp();
demo.Test();

}

}

2015年10月11日 星期日

[Java]如何求N個整數的最大公因數

這個問題我認為原理非常的簡單...我在學Java的第一週就可以把他做出來
不過後來時間久了就忘記要把這個問題的解法丟上來

趁著現在比較有空的時間把教學簡單的打一下


首先從兩個整數的最大公因數開始
整數的最大公因數就是能夠同時整除他們的最大整數
求最大公因數的方法有很多種,其中一種方法叫做輾轉相除法
我們直接拿30跟18這兩個整數做例子
30/18=1餘12
18/12=1餘6
12/6=2

由於6整除了,所以30跟18的最大公因數就是6

從例子我們知道做法就是如果兩數沒有整除,就把原來的除數當做被除數,把餘數當做除數繼續除下去,直到兩數整除為止

於是我們可以知道求a,b兩數的最大公因數,相當於求b與a,b的餘數的最大公因數
以下就是簡單的範例,我們用一般的while迴圈展示輾轉相除法的演算法
另外使用遞迴當做參考

public class Gcd {



 public static void main(String[] args) {  

  //demo1

  System.out.println(gcd(18,12));

    

  //demo2

  System.out.println(gcd2(30,18));  



 }



 public static int gcd(int m, int n){

  int result = 1;

  while(m%n!=0){

   result=n;   

   n=m%n;

   m=result;

  }

  result=n;

  

  return result;

 }

 

 public static int gcd2(int m, int n){

  if(m%n==0){

   return n;

  } else {

   return gcd2(n,m%n);

  }  

 } 

}


接下來三個整數的最大公因數就是將前兩個最大公因數跟第三個數字做最大公因數
四個整數的最大公因數則是將三個整數的最大公因數與第四個數字做最大公因數...
以此類推
以下程式碼就只用while迴圈當範例,遞迴的寫法請自情參考兩個整數的程式碼


public class Gcd {



 public static void main(String[] args) {

  

  //demo1

  int[] x = new int[] {18,12,30};  

  System.out.println(dogcd(x));  

  

  //demo2 

  int[] y = new int[] {15,18,30,42,9};

  System.out.println(dogcd(y));

 }

 

 public static int dogcd(int[] input){

  for(int i=0;i<input.length-1;i++){

   input[i+1] = gcd(input[i],input[i+1]);

      

  }  

  return input[input.length-1];

 }



 public static int gcd(int m, int n){

  int result = 1;

  while(m%n!=0){

   result=n;   

   n=m%n;

   m=result;

  }

  result=n;

  

  return result;

 }
  

}

2015年10月6日 星期二

[Java]費氏數列

費氏數列(fibonacci sequence)是程式語言中常見的遞迴範例
他的每一項分別是:
A0=0
A1=1
AN=A(N-1)+A(N-2)
也就是A2以後,每一項的值就是前兩項的值相加
使用遞迴的方法可以把費式數列的值做出來
以下是遞迴的範例


public class FibonacciTest {

  

 public static void main(String[] args) {
    
   System.out.println(fibonacci(50));    

 }

  

 public static long fibonacci(int x){

  if(x==1||x==2){

   return 1;

  }else {

   return fibonacci(x-1)+fibonacci(x-2);

  }  

 }  

}




但是使用遞迴的效能很差
所以如果有人提出這個問題
又沒有特別規定要用遞迴做出來的話
建議使用一般的迴圈來解決這個問題
效能會有明顯的改善
以下是範例


public class FibonacciDemo {



 public static void main(String[] args) {

  

  FibonacciDemo demo = new FibonacciDemo();

  System.out.println(demo.fibonacci(50));  

 }

 

 public long fibonacci(int n){

  if(n==0){

   return 0;

  } else {

   long x_1 = 0;

   long x_2 = 1;

   for(int i=0;i<n;i++){

    if(i>0){

     x_2 = x_2 + x_1;

     x_1 = x_2 - x_1;

    }
   
   }

   return x_2;

  }

 }


}


2015年9月13日 星期日

[Java]StringBuilder與StringBuffer常用的方法簡介

這兩個class主要是在字串串接很多字的時候,
由於String字串池的關係會生出很多字串物件,
為了節省記憶體就會使用使用StringBuilder跟StringBuffer

StringBuilder跟StringBuffer的方法幾乎一模一樣
關於兩者的差別請見右邊連結:連結在此
常用的方法有:

append() :這個方法是將字串接在字串的最後方
insert():這個方法可以將字串接在你指定的位置
indexOf():這個方法是尋找某個字串在現在這段字的那個位置,可以拿來搭配insert使用
reverse():可以將整個字串反轉順序,雖然我幾乎用不到
toString():就是將串好的字轉成字串輸出
length():現有的字串長度

以下就是簡單的範例



public class StringBuilderDemo {

 public static void main(String[] args) {
  
  StringBuilder sb = new StringBuilder();

  sb.append("This is ");

  sb.append("a star");

  System.out.println(sb.toString());//This is a star

  System.out.println(sb.length());//14

  System.out.println(sb.indexOf("star"));//10

  sb.insert(sb.indexOf("star"), "new ");

  System.out.println(sb.toString());//This is a new star

  System.out.println(sb.reverse().toString());//rats wen a si sihT

 }

}

2015年9月7日 星期一

[Java]&&, || 與 &,| 運算子的不同

在Java上有&&跟||這個運算子與 & 跟 | 這個運算子

雖然&&跟&都是and運算
||和 | 都是or運算
不過實際運作起來是有差別的
&&跟||運算時如果結果已經確定運算結果
他們就不會再撿查下一個敘述是否為真

比如說 A&&B
如果A已經是false了,那麼不管B是true or false,結果都是false
所以就不會去檢查B

像是下面的程式碼可以運作

public class Demo {



 public static void main(String[] args) {

  String a = null;

  String b = "a";



  System.out.println(a==null||a.equals(b));//true

  System.out.println(a!=null&&a.equals(b));//false

 }

}


然後&跟|不管如何兩個序述都會檢查
所以下面的程式碼就會跳nullpoint

 public static void main(String[] args) {

  String a = null;

  String b = "a";



  System.out.println(a==null|a.equals(b));//true

  System.out.println(a!=null&a.equals(b));//false

 }

}

2015年9月1日 星期二

[Java]Sting、StringBuffer與StringBuilder

String因為字串池的關係,當你做出下面的運算時

Sting x = "a"+"b"+"c"+"d";
JVM會產生四個String物件,所以當你有字要串起來時為了節省記憶體通常不會使用String
而會使用StringBuffer與StringBuilder
那麼StringBuffer與StringBuilder到底要選什麼用呢?首先要了解兩者的不同
StringBuffer與StringBuilder第一個不同在於兩者的效能有明顯的差距
如下面範例

public class Test {

    public static void main(String[] args) {

        int N = 77777777;

        long t;

        {

            StringBuffer sb = new StringBuffer();

            t = System.currentTimeMillis();

            for (int i = N; i --> 0 ;) {

                sb.append("");

            }

            System.out.println(System.currentTimeMillis() - t);

        }

        {

            StringBuilder sb = new StringBuilder();

            t = System.currentTimeMillis();

            for (int i = N; i --> 0 ;) {

                sb.append("");

            }

            System.out.println(System.currentTimeMillis() - t);

        }

    }

}


那麼要建立String就用StringBuilder囉?
其實不見得
StringBuffer是synchronized,也就是在多執行緒上他比較不會發生錯誤
StringBuilder在多執行緒的程式上就必須注意Thread safe的問題

2015年8月29日 星期六

[JAVA]Java物件使用 == 與 .equals方法的差別

在Java基礎資料形別(byte,short,int,long,boolean,char,float,double)的==判斷式就是檢查兩個資料的值是否相等,但是到物件參考形別是==表示的是兩個物件參考的記憶體位置是否一樣

比如說
Object A = new Object();
Object B = new Object();

A==B的時候就是false


Object A = new Object();
Object B = ObjectA;

A==B的時候結果是true

在這邊要檢查兩個物件裡面的參數是否一樣的時候就需要要用到.equals方法
這個方法是java.lang.Object的方法
所以在編寫class時可以被覆寫
你可以自行定義什麼情況下兩個class內容是相等的
就可以使用 A.equals(B)這種語法來判斷兩個物件是不是你定義的相同

String雖然是物件,但是因為字串池的關係

String A ="abc";
String B ="abc";
兩個字串物件會指到同一個記憶體位置
A==B的結果會是true

但是如果想要程式用一些method撈值用String存的話
A==B兩個字串即使值是一樣的結果也會顯示false
這時使用A.equals(B);
比較不會發生問題造成維護上的困擾

2015年8月6日 星期四

[Java]Java的Static block與建構子(constructor)

Static block是Java中的一個用來初始化Static屬性的功能
一個Class中的Static block只會執行一次
建構子(constructor)則是每次new一個物件的時候就會執行一次

建構子的名稱要與class名稱相同,並且不允許同樣參數的建構子

使用static block與constructor的方法範例如下

public class StaticInitDemo {

 private static String message;

 private String message2;

 

 static{

  System.out.println("static block initialize");

  setMessage("This is an demo of static block");

 }

 

 public StaticInitDemo(){

  System.out.println("constructor initialize");

  setMessage2("This message is generate as constructor");  

 }

 public StaticInitDemo(String str){

  System.out.println("constructor with parameter initialize");

  setMessage2("str="+str);  

 }

 public static String getMessage() {

  return message;

 }

 public static void setMessage(String message) {

  StaticInitDemo.message = message;

 }

 public String getMessage2() {

  return message2;

 }

 public void setMessage2(String message2) {

  this.message2 = message2;

 }

 

}//end of class



public class StaticBlockDemo {



 public static void main(String[] args) {

  System.out.println("Test begin\n");

  

  System.out.println("StaticBlock:");

  System.out.println(StaticInitDemo.getMessage());

  

  System.out.println("\n"+"Constructor:");

  StaticInitDemo demo = new StaticInitDemo();  

  System.out.println(demo.getMessage2());

  StaticInitDemo demo2 = new StaticInitDemo(); 



 }



}//end of class


執行結果為:
Test begin

StaticBlock:
static block initialize
This is an demo of static block

Constructor:
constructor initialize
This message is generate as constructor
constructor initialize


2015年7月31日 星期五

[Java]計時器功能:Timer、TimerTask

使用定時或固定時間間隔有幾個方法
一個是Thread.sleep();

方法內填延遲的毫秒數就可以讓程式延遲後再執行

另一種就是使用Timer class
這個class的schedule方法有兩個變數以及三個變數
詳細的使用方法請見範例
範例有兩個class,將要執行的程式碼寫在繼承TimerTask的class裡面
然後使用Timer裡面你要用的方法就可以使用定時執行程式的功能了


import java.text.SimpleDateFormat;
import java.util.*;
public class TimerDemo {
 SimpleDateFormat sdf = new SimpleDateFormat("hh時mm分ss秒 yyyy年MM月dd日");
    public static void main(String[] args) {
        TimerDemo timerDemo = new TimerDemo();
        System.out.println("方法一示範");
        timerDemo.testScheduleDelay();
        System.out.println("方法二示範");
        timerDemo.testScheduleDelayAndPeriod();
        System.out.println("方法三示範");
        timerDemo.testScheduleDateAndPeriod();
   }
    
    void testScheduleDelay(){
        Timer timer = new Timer();
        System.out.println("延遲時間:3秒");
        System.out.println("現在時間:" + sdf.format(new Date()));
        // schedule(TimerTask task, long 延遲時間)
        timer.schedule(new DateTask(), 3000);
        
        try {
            Thread.sleep(10000);
        }
            catch(InterruptedException e) {
        }
        timer.cancel();
        System.out.println("結束時間:" 
            + sdf.format(new Date()) + "\n");
    }
    
    void testScheduleDelayAndPeriod(){
        Timer timer = new Timer();
        System.out.println("延遲時間:3秒, 時間間格:2秒");
        System.out.println("現在時間:" 
            + sdf.format(new Date()));
        
        // schedule(TimerTask task, long 延遲時間, long 時間間格)
        timer.schedule(new DateTask(), 3000, 2000);
       
        try {
            Thread.sleep(10000);
        }
            catch(InterruptedException e) {
        }
        timer.cancel();
        System.out.println("結束時間:" 
            + sdf.format(new Date()) + "\n");
    }
    
    void testScheduleDateAndPeriod(){
        Timer timer = new Timer();
        
        // 設定填入schedule中的 Date firstTime 為現在的15秒後
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.SECOND, calendar.get(Calendar.SECOND)+15);
        Date firstTime = calendar.getTime();     
       // 也可用 simpleDateFormat 直接設定 firstTime的精確時間
       // SimpleDateFormat dateFormatter = 
        //      new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");  
        // Date firstTime = dateFormatter.parse("2011/12/25 13:30:00");
        
        System.out.println("現在時間:" 
            + sdf.format(new Date()));
        System.out.println("設定執行 Date 為15秒後:" 
            + firstTime +", 時間間格:3秒");
                
        // schedule(TimerTask task, Date 開始時間, long 時間間格)
        timer.schedule(new DateTask(), firstTime, 3000);
                
        try {
            Thread.sleep(30000);
        }
            catch(InterruptedException e) {
        }
        timer.cancel();
        System.out.println("結束時間:" 
            + sdf.format(new Date()) + "\n");
    }
}

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;
//繼承timertask將要執行的動作覆寫在run裡面
public class DateTask extends TimerTask {
 SimpleDateFormat sdf = new SimpleDateFormat("hh時mm分ss秒 yyyy年MM月dd日");
 private int i=0;
    @ Override
    public void run() {
     i++;
        System.out.println(i+":"+ sdf.format(new Date()));
    }
}

2015年7月17日 星期五

[Java]static關鍵字


Static這個關鍵字有些人理解是維一的意思,其實他指的是這個屬性是放在類別(class)那一層,不隨著物件(object)的變更去改動的,非static的屬性,每一個物件會有一個值
所以物件A的值改變了,物件B的值並不會改變,但是static的屬性你不管使用物件A、物件B去更動他的值,他的值就是固定在類別上,所以會一起更動,所以他有另一個特性:可以用類別名稱去呼叫,最簡單的例子就像是Math.pow()這種方法(method)一樣,他是static的,如果這個方法還是自己類別的static方法,甚至可以省略類別名稱直接打上方法,以下是程式範例






public class StaticDemo {

public static void main(String[] args) {
StaticDemo obj1 = new StaticDemo();
StaticDemo obj2 = new StaticDemo();
System.out.println("method1:");
System.out.println(obj1.method1());//1
System.out.println(obj2.method1());//1

System.out.println(obj1.method1());//2
System.out.println(obj2.method1());//2

System.out.println("method2:");
System.out.println(StaticDemo.method2());//1
System.out.println(method2());//2
System.out.println(obj1.method2());//3
System.out.println(obj2.method2());//4

}

private int i1=0;
public int method1(){
i1++;
return i1;
}

private static int i2=0;
//private int i2=0;  //this will compile error
public static int method2(){
i2++;
return i2;
}
}

特別注意的是static的方法裡,要使用這個class的屬性的話,那個屬性就必須是static
不然你就要在裡面new一個自己類別的物件出來使用這個屬性,像是public static void main裡面那樣

2015年7月10日 星期五

[Java]台灣身份證字號產生器

關於驗證身份證字號的方法請參考 身份證字號驗證
這個產生器的原理是隨機產生身份證字號,最後用身份證字號公式計算出最後一碼的數字為多少,再將整個字串拼起來

public class ID {

 

 public static void main(String[] args) {

  

  String idNumber = ID.IDRandom();

  System.out.println("-------IDGeneratorTest-------");

  System.out.println(idNumber);

  System.out.println("-------IDGeneratorTest-------");





 }

 

 

 public String str="";



 public static String IDRandom(){

  



  char[] idByChar = new char[9];

  

  String iDString = "";

     

   //隨機生成A~Z

   int x = (int)Math.floor(Math.random()*26+65);

   idByChar[0] = (char) x;

   

   //隨機生成1~2

   idByChar[1] = (char)(int)Math.floor(Math.random()*2+49);

   

   //隨機產出第3~第9個數字

   for (int i=2;i<9;i++){

    idByChar[i] = (char)(int)Math.floor(Math.random()*10+48);

   }   

   

   //第十個數字由前九個字組成

   

   iDString = new String(idByChar);

   ID id = new ID(iDString);

   int[] temp = new int[10];

   temp[0]= id.D0();

   

   for(int i=1;i<9;i++){

    temp[i]=id.DNumber(i); 

   }

   //最後將字組起來送出

   temp[9]=(10-(id.CheckCode(temp)%10))%10;

   iDString = iDString+temp[9];



   

  return iDString;

 }

 

 //D0是找第一個字的代碼,因為有些數字不規律所以要用很多if做判斷

 private int D0(){

  int D00=0;

  int temp = this.str.codePointAt(0);

  

  if(72>=temp && temp>=65)

  {

   D00 = temp-55;

  }else if(78>=temp&&temp>=74){ 

   D00 = temp-56;

  }else if(86>=temp&&temp>=80){

   D00 = temp-57;

  }else if(90>=temp&&temp>=88)

  {

   D00 =temp-58;

  }

  switch(temp){

  case 74 :

   D00 =temp-39;

  

  case 79 :

   D00 =temp-44;

   break;

  

  case 87 :

   D00 =temp-55;

   break;

  default:

   break;

  }

  

  //這邊是用來檢查使用者輸入小寫時的判斷式

  if(104>=temp && temp>=97)

  {

   D00 = temp-87;

  }else if(110>=temp&&temp>=106){ 

   D00 = temp-88;

  }else if(118>=temp&&temp>=112){

   D00 = temp-89;

  }else if(122>=temp&&temp>=120)

  {

   D00 =temp-90;

  }

  switch(temp){

  case 106 :

   D00 =temp-71;

  

  case 111 :

   D00 =temp-76;

   break;

  

  case 119 :

   D00 =temp-87;

   break;

  default:

   break;

  }   

  return D00;

 }



 

 //DNumber用來撿查第2~第10個字,所以判斷式簡單許多

 private int DNumber(int i){

  int D1=100;

  //這個初始100只是用來表示如果他不是0~9這個值就顯示100

  //也可以用其他數值來表示,但是要避免使用0~9

  int temp = this.str.codePointAt(i);

  if(57>=temp&&temp>=48){

   D1=temp-48;

  }

  return D1;

 }

 

  

 //這邊只是輔助計算身份證是否正確

 private int CheckCode(int[] X){

 

  int x1=Math.floorDiv(X[0],10);

  int x2=X[0]%10;

  

  

  int Y=x1+(9*x2)+(8*X[1]);

   for(int i=2;i<=8;i++)

   {

    Y=(9-i)*X[i]+Y;

   }

  

  return Y;

 }

     

 public void ChangeID(String s){

  this.str=s;

 }

 

 public ID(String s){

  this.str=s;

 }

 public ID(){

  

 }

}

2015年7月9日 星期四

[Java]台灣身份證字號驗證器

關於身份證的規則請查照中華民國國民身份證wiki
codePointAt(i)這個方法是用來取出字串第i位置的字的編碼(左邊數來第一個字位置為0)
ASCII table請參照此ASCII wiki

如果要找的是產生器請看此:身份證字號產生器


public class ID {

 

 public static void main(String[] args) {




  //將要測試的身份證字號填入此
  ID id = new ID("A123456789");
  // --------------------------


  //IDcheack回傳的是true跟false

  if(id.IDCheak()){

   System.out.println("This ID Number is leagal");

  }else {

   System.out.println("This ID Number is illeagal");

  }

  

 }

 

 

 

 public String str="";



 //檢查是否為身份證字號並回傳True或False

 public boolean IDCheak(){

   



  ID id= new ID(this.str);

  int[] D= new int[10];

   

   

  //身份正字號長度必須為10個字

  if(str==null || str.length()!=10)

  {

   return false;

  }

  //首字為英文,然後第二位數為1~2

  if(!(id.D0()!=0&&id.DNumber(1)>0&&id.DNumber(1)<3)){

   return false;

  }      

  //put the first code and the second code

  D[0] =id.D0();

  D[1] =id.DNumber(1);     

  //剩下數值應為0~9

  for(int k=2;k<10;k++){

   if(!(id.DNumber(k)>=0&&id.DNumber(k)<=9))

   {

    return false;

   }

   

   D[k] =id.DNumber(k);

  }

  

   

  int cheakCode =0;

   

  cheakCode=(10-(id.CheckCode(D)%10))%10;

   if(cheakCode!=D[9])

   {

    return false;

   }

   

  return true;

 }

 

 //D0是找第一個字的代碼,因為有些數字不規律所以要用很多if做判斷

 private int D0(){

  int D00=0;

  int temp = this.str.codePointAt(0);




  if(72>=temp && temp>=65)

  {

   D00 = temp-55;

  }else if(78>=temp&&temp>=74){ 

   D00 = temp-56;

  }else if(86>=temp&&temp>=80){

   D00 = temp-57;

  }else if(90>=temp&&temp>=88)

  {

   D00 =temp-58;

  }

  switch(temp){

  case 74 :

   D00 =temp-39;

  

  case 79 :

   D00 =temp-44;

   break;

  

  case 87 :

   D00 =temp-55;

   break;

  default:

   break;

  }

  

  //這邊是用來檢查使用者輸入小寫時的判斷式

  if(104>=temp && temp>=97)

  {

   D00 = temp-87;

  }else if(110>=temp&&temp>=106){ 

   D00 = temp-88;

  }else if(118>=temp&&temp>=112){

   D00 = temp-89;

  }else if(122>=temp&&temp>=120)

  {

   D00 =temp-90;

  }

  switch(temp){

  case 106 :

   D00 =temp-71;

  

  case 111 :

   D00 =temp-76;

   break;

  

  case 119 :

   D00 =temp-87;

   break;

  default:

   break;

  }   

  return D00;

 }



 

 //DNumber用來撿查第2~第10個字,所以判斷式簡單許多

 private int DNumber(int i){

  int D1=100;

  //這個初始100只是用來表示如果他不是0~9這個值就顯示100

  //也可以用其他數值來表示,但是要避免使用0~9

  int temp = this.str.codePointAt(i);

  //use variable to determine the code of the i-th character of the string

  if(57>=temp&&temp>=48){

   D1=temp-48;

  }

  return D1;

 }

 

 

 

 

 

 //這邊只是輔助計算身份證是否正確

 private int CheckCode(int[] X){

 

  int x1=Math.floorDiv(X[0],10);

  int x2=X[0]%10;

  

  

  int Y=x1+(9*x2)+(8*X[1]);

   for(int i=2;i<=8;i++)

   {

    Y=(9-i)*X[i]+Y;

   }

  

  return Y;

 }

     

 public void ChangeID(String s){

  this.str=s;

 }

 

 public ID(String s){

  this.str=s;

 }

 public ID(){

  

 }

}

2015年7月7日 星期二

[Java]整數轉字串/字元,字串/字元轉整數(int to String/char and String/cha to int)

在Java中,如果要將數字轉換成字元或字串,或是反過來將字元字串轉成數字的時候,要充份了解互相轉換的機制,比如說
(char)49會將49以編碼的方式轉換,於是顯示出來的數字是1

如果想要將數字轉換成對應的字元,我通常會先轉換成字串再轉成字元
或許有更好的方法,以下列出各種字元/字串對應成整數的方法


public class demo {



 public static void main(String[] args) {



  int i = 1;

  System.out.println("int to String");//整數轉換成字串

  System.out.println(Integer.toString(i));//1 String

  System.out.println(""+i);//1 String

  System.out.println("int to char");

  System.out.println("");



  int i2 = 49;

                //整數強制型別轉換成字元會當成編碼解讀

  System.out.println((char)i2);//1 char

                //轉換成字串在拆成字元

  System.out.println((""+i2).charAt(0));//4 char

  System.out.println((""+i2).charAt(1));//9 char

  System.out.println("");



  char ch = '1';

  System.out.println("char to int");

                //強制型別轉換會將字元轉換成編碼數字

  System.out.println((int)ch);//49 int

                //Character.getNumbericValue()才能取到原來數字

  System.out.println(Character.getNumericValue(ch));//1 int

  System.out.println("");

  

  String str ="1";

  System.out.println("String to int");

                //Inter.parseInt()可將字串轉為數字

  System.out.println(Integer.parseInt(str));//1 int

                //需要讀取字串的編碼數字需要用codePointAt()

  System.out.println(str.codePointAt(0));//49 int, ascii of 1

  System.out.println("");

 }



}

2015年7月3日 星期五

[Java]日期格式化-SimpleDateFormat

在Java裡面時間轉換格式是一個很麻煩的課題

因為時間的表達方法各種各樣,有些人寫2015/07/03,有些人寫07/03/2015,有些人寫July 03 /2015,麻煩的是07/03到底表示的是三月七號還是七月三號呢?
所以在Java裡面有個class來格式化日期的類別叫SimpleDateFormat
關於格式的設定詳情請看api文件Date and Time Patterns
格式設定的語法大概如下
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
注意大寫的M代表月份,小寫的m代表的是分鐘

主要的方法有兩個:parse跟format
parse是把字串轉換成日期型態
format是將日期型態轉換成字串

最後特別提一個setLenient方法,他會另外檢查你輸入的值合不合法
預設值是true,改為false的話你輸入15月他會跳出錯誤訊息,如果是true輸入15月或是40日這種他會自動轉成第二年三月或是第二個月10日等等。

以下是範例程式碼

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Date;





public class DateFormatDemo {



 public static void main(String[] args) throws ParseException {



  SimpleDateFormat sdfor = new SimpleDateFormat("yyyy/MM/dd");

  Date date = sdfor.parse("2015/07/03");

  System.out.println(date);//Fri Jul 03 00:00:00 CST 2015

  System.out.println(sdfor.format(date));//2015/07/03

  

  sdfor = new SimpleDateFormat("yyyy/mm/dd");

  date = sdfor.parse("2015/07/03");

  System.out.println(date);//Sat Jan 03 00:07:00 CST 2015

  

  sdfor = new SimpleDateFormat("yyyy");

  date = sdfor.parse("2015");

  System.out.println(date);//Thu Jan 01 00:00:00 CST 2015

  System.out.println(sdfor.format(date));//2015

  

  sdfor = new SimpleDateFormat("MM月");

  date = sdfor.parse("12月");

  System.out.println(date);//Tue Dec 01 00:00:00 CST 1970

  System.out.println(sdfor.format(date));//12月

  

  

  SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");  

  System.out.println(sdf.parse("1985/15/48"));

  

//  sdf.setLenient(false);

//  System.out.println(sdf.parse("1985/15/48"));//執行會跳exception

 }



}//end of class

2015年6月30日 星期二

[Java]樂透抽取程式

這是一個很經典的題目,從1~49,
隨機抓取六個不同的數字
在Java上要隨機抽取數字不難,用 java.util.Random()或是Math.random()都可以 
我是習慣用Math.random()這個方法
Math.random()的取值是0<=Math.random()<1,所以要取1~49必須要寫成
Math.random()*49+1,那個範圍在0<=Math.random()*49+1<50
接著傳出的數值是double,所以要轉成int,取六個數字的程式碼就會變成這樣

  int[] random = new int[6];
for(int i=0;i<6;i++){
random[i]=(int)Math.floor(Math.random()*49+1);

}

接著上述抽取的數字會有重覆的問題,最簡單的解法就是去找之前的數字有沒有重覆,有重覆就重抽一個數字,如範例:

public class Random {

public static void main(String[] args) {

int[] random = new int[6];
for(int i=0;i<6;i++){
random[i]=(int) Math.floor(Math.random()*49+1);
for(int j=0;j<i;j++){//檢查有無重覆
if(random[i]==random[j]){//有重覆重抽一次
i--;
break;
}
}
}
System.out.println("result is:");
for(int x=0;x<6;x++){
System.out.print(random[x]+" ");
}
}
}


這個方法有個小缺點,比如說1000顆球取一千顆時,後面會重覆過多一直重抽數字
如果要解決這個情形可以用以下的解法
比如說10顆球抽3顆。一開始我們先將陣列排好成下面這樣
1 2 3 4 5 6 7 8 9 10
隨機1~10,假設抽到的是7號,我們就將第1個位置跟第7個位置交換
7 2 3 4 5 6 1 8 9 10
接著隨機2~10,假設抽到的是5號,接著將第2個位置跟第5個位置交換
7 5 3 4 2 6 1 8 9 10
再來隨機3~10,抽到的如果是5號,就將第3個位置跟第5個位置交換
7 5 2 4 3 6 1 8 9 10
最後抽到的三顆球就是打底線這三顆

最後我將總共的球數和抽取球數寫成鍵盤輸入,程式碼如下:

public class RandomApi {

public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("please input the number of balls:");
int balls= input.nextInt();
System.out.println("please input how many balls you want to pick:");
int picks= input.nextInt();
int [] result = RandomNumberPerduce(balls, picks);
for(int i=0;i<picks;i++){
System.out.print(result[i]+" ");
}
input.close();
}


//random value
private static int[] RandomNumberPerduce(int x1, int x2){

int[] random= new int[x1];
if(x2>x1){
System.out.println("Sorry, the number of balls is less then the number you want to pick up");
return random;
}
for(int i=0;i<x1;i++){
random[i]=i+1;
}

for(int i=0;i<x2;i++)
{
int result=(int) Math.floor(Math.random()*(x1-i)+i);
int temp = random[result];
random[result]=random[i];
random[i]= temp;

}
return random;
}


}//end of class

2015年6月29日 星期一

[Java]讀取鍵盤輸入

Java裡面讀取鍵盤輸入的方法有兩種,一種是Scanner,一種是InputStreamReader


首先我們先介紹Scanner
Scanner可以生成一個物件然後用.next方法輸入string,或是用.nextInt方法等等輸入不同的資料型別。這方法會有一個

import java.util.Scanner;

public class scannerDemo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);

System.out.println("Please type some words:");
String str = input.next();
System.out.println("The words you have typed is:"+str);

System.out.println("Please type an integer x:");
int x = input.nextInt();
int sum=0;
for(int i=1;i<=x;i++){
sum=sum+i;
}
System.out.println("The sum of 1 to x is:"+sum);
input.close();

}

}



再來是InputStreamReader
因為要使用一次讀一行的指令所以需要用到BufferReader
new BufferedReader(new InputStreamReader(System.in))這種語法其實是要看成
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader bfr  = new BufferedReader(isr);兩行
不過這種寫成一行的語法沒有isr可以用就是了(雖然也沒有要用),
這方法按ctrl+z可以終止輸入。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class inputStreamReaderDemo {

public static void main(String[] args) throws IOException {
BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Input string and exit by ctrl+z");
String str = bfr.readLine();

//while intput "ctrl+z" the program will stop
while(str!=null){
System.out.println(str);
str = bfr.readLine();
}
System.out.println("End of input");
bfr.close();


}

}

2015年6月27日 星期六

[Java]如何列出100以下的所有質數


程式語言另一個常見的作業就是跟質數當朋友
來找找一個數是不是質數,或找出100以內所有的質數這種題目


今天談談怎麼找出1到100的所有質數吧
解決這個問題前先處理下面兩個問題
如何印出2到100(最小的質數是2),答案我想剛學過迴圈的應該都做的出來,就是

  for(int i=2;i<=100;i++){
   System.out.println(i);
  }

接下來只要把1到100每個數都檢查他是不是質數就可以了,如何檢查一個整數不是質數呢
首先質數的定義是大於一的整數,除了1跟自己本身以外,沒有其他因數
所以呢最簡單的想法就是設一個boolean去處理他
舉個例子好了,檢查7是否為質數

  boolean isPrime;
  for(int i=2;i<7;i++){

   if(7%i==0){
    isPrime=false;
   
   }
  }

所以這個迴圈就會檢查二以上,比7小的數字有沒有整除他,有整除表示7有其他因數 就不是質數囉,當然最後因為2到6並不會整除7,最後isPrime的值就會是true,所以就可以抓出7是質數。

有了上述兩件事我們就可以開始把兩個程式合併起來

  boolean isPrime;
  
  for(int i=2;i<=100;i++){
   isPrime=true;
   
   for(int j=2;j<i;j++){

    if(i%j==0){
     isPrime=false;
    
    }
   }
   
   if(isPrime){
    System.out.print(i+" ");
    
   }
   
  }

 }


注意兩個迴圈不太一樣,第一個迴圈是<=100,因為你檢查的時候要檢查的是2到100
但是第二個迴圈要檢查的時候不能檢查自己,因為自己一定被自己整除,
弄成小於等於的話不管怎樣isPrime一定會false,那些該用小於等於,那些東西該用小於;,必須想的很清楚,不然程式就會花式秀bug,你還不知道怎麼死的

做到這看似完成了,但是其實這個方法從2檢查到100大概要做快5000次
(第二個for迴圈執行了4851次)
並不是很有效率,事實上你檢查的時候如果不是質數並不會用繼續檢查其他數字,可以將迴圈做break跳出,另外,在數學的觀點來講,X這個整數如果你檢查到根號X都沒有質因數,他就是質數了,故程式碼可以改成


  boolean isPrime;
  
  for(int i=2;i<=100;i++){
   isPrime=true;
   
   for(int j=2;j<Math.sqrt(i);j++){

    if(i%j==0){
     isPrime=false;
     break;
    }
   }
   
   if(isPrime){
    System.out.print(i+" ");
    
   }
   
  }

到了這邊第二個for迴圈的進入次數就被壓縮到剩下232次,應該可以感受到用不同的方法去實做程式,不去想辦法降低程式運算次數,只靠靠硬體硬做效能會有多大的差距了。
我解出上述答案後過了幾天我就在思考,如果我只檢查比根號x小的所有質數,會不會更快呢?於是我寫出了下面的class

public class ShowPrime {



 public static void main(String[] args) {

  showPrime(100); 

 

 }



 



 

 public static void showPrime(int k){

  int[] prime = new int[(k+1)/2];  

  prime[0] = 2;//the first prime is 2

  int pCount=0;

  int count=0;

  boolean isPrime=true;

  for(int i=2;i<=k;i++){

   

   isPrime=true;

   for(int j=0;prime[j]<=Math.sqrt(i);j++){

    

    if(i%prime[j]==0){

     isPrime=false;

     break;

    }

   }

   if(isPrime){

    prime[pCount]=i;

    pCount++;

    System.out.print(i+"\t");

    count++;

    if(count%10==0){//create new line per 10 numbers

     System.out.println("");

    }

      

   }

     

  }

  

 }

 

}

這邊會另外需要記憶體去儲存質數,所以在100的時候我不覺得這個方法會比較好,但是在數字更大的時候這個方法執行效能應該就會比較好了


最後提一下,我認為這邊的方法並不是最快速的方法,但是初學程式的新手最需要學習的概念,並不是只是程式語法本身,語法很重要,不去學會你完全不能開始寫程式,但是解決問題的方法也是另一個重要課題,學習如何依照使用者的要求,去找出解決問題的方案,並且找出解決方案後,去思考能不能改善他的效能,所以這個問題除了把答案解出來以外,試著去思考如何撿少運算次數一定有幫助的