22天-01-GUI
GUI:Graphical User Interface 圖形用戶接口
Java爲GUI提供的對象都存在java.Awt和javax.Swing兩個包中
CLI:Common line User Interface 命令行用戶接口java
Awt:Abstract Window ToolKit(抽象工具包),須要調用本地系統方法實現功能,屬於重量級控件。
Swing:在Awt的基礎上,創建的一套圖形界面系統,其中提供了更多的組件,並且徹底由Java實現,加強了移植性,屬於輕量級控件。正則表達式
繼承關係:
Component Container Window、Panel Frame、 Dialog FileDialog
Button、Label、Checkbox、TextComponent TextArea、TextField數據庫
Container:爲容器,是一個特殊的組件,該組件能夠經過add方法添加其餘組件進來。
Swing繼承關係圖:編程
佈局:容器中的組件排放方式
常見的佈局管理器:
FlowLayout: 流式佈局管理器,從左到右的順序排列,Panel默認的佈局管理器
BorderLayout: 邊界佈局管理器,中東南西北,Frame默認的佈局管理器
GridLayout: 網格佈局管理器,規則的矩陣
CardLayout: 卡片佈局管理器,選項卡
GridBagLayout: 網格包佈局管理器,非規則的矩陣瀏覽器
事件監聽機制的特色:
1.事件源:就是awt包或者swing包中的那些圖形界面組件。
2.事件:每個事件源都有本身特有的對應事件和共性事件。
3.監聽器:將能夠觸發某個事件的動做(不止一個動做)都已經封裝到了監聽器中。
4.事件處理tomcat
製做可執行文件:
1.製做配置文件config.txt,冒號後面必須有一個空格,最後一行必須換行
Main-Class: myPackage.ExecuteFile
2.先編譯 javac -d packagePath File.java
javac -d . ExecuteFile.java
3.打包 jar -cvfm package.jar config.txt PackageDirectory
jar -cvfm ExecuteFile.jar config.txt myPackage網絡
ExecuteFile示例: 多線程
1 package myPackage; 2 import java.util.*; 3 import java.io.*; 4 import java.awt.*; 5 import java.awt.event.*; 6 7 public class ExecuteFile 8 { 9 private Frame fr; 10 public static void main(String[] args)throws IOException 11 { 12 new ExecuteFile(); 13 } 14 public ExecuteFile() 15 { 16 init(); 17 } 18 private void init() 19 { 20 fr=new Frame("Demo"); 21 fr.setSize(400,500); 22 fr.setLocation(200,100); 23 fr.setVisible(true); 24 fr.addWindowListener(new WindowAdapter(){ 25 public void windowClosing(WindowEvent e) { 26 System.exit(0); 27 } 28 }); 29 } 30 }
1 Main-Class: myPackage.ExecuteFile
23天-01-網絡編程併發
網絡基礎知識:
IP地址:網絡中設備的標識
本地迴環地址: 127.0.0.1 主機名:localhost
端口號:用於標識進程的邏輯地址,不一樣進程的標識。有效端口號:0~65535,其中0~1024爲系統使用或者保留端口。
Internet默認端口: 80 tomcat默認端口:8080 數據庫默認端口:3306
傳輸協議:計算機之間通訊的規則。常見協議:TCP,UDPide
OSI模型: 應用層,表示層,會話層,傳輸層,網絡層,數據鏈路層,物理層
TCP/IP模型: 應用層, 傳輸層,網絡層, 主機至網絡層
UDP:將數據及源和目的封裝成數據包中,不須要創建鏈接,每一個數據報的大小限制64k內,因無鏈接,是不可靠協議,不須要創建鏈接,速度快。
TCP:創建鏈接,造成傳輸數據的通道,在鏈接中進行大量數據傳輸,經過三次握手完成鏈接,是可靠協議,必須創建鏈接,效率稍低。
TCP與UDP區別:
一、TCP面向鏈接(如打電話要先撥號創建鏈接); UDP是無鏈接的,即發送數據以前不須要創建鏈接
二、TCP提供可靠的服務。也就是說,經過TCP鏈接傳送的數據,無差錯,不丟失,不重複,且按序到達; UDP盡最大努力交付,即不保證可靠交付
三、TCP面向字節流,其實是TCP把數據當作一連串無結構的字節流; UDP是面向報文的,UDP沒有擁塞控制,所以網絡出現擁塞不會使源主機的發送速率下降(對實時應用頗有用,如IP電話,實時視頻會議等)
四、每一條TCP鏈接只能是點到點的; UDP支持一對一,一對多,多對一和多對多的交互通訊
五、TCP首部開銷20字節; UDP的首部開銷小,只有8個字節
六、TCP的邏輯通訊信道是全雙工的可靠信道; UDP則是不可靠信道
Socket:是爲網絡服務提供的一種機制,通訊的兩端都有Socket,數據在兩個Socket間經過IO傳輸。
注:IP地址段中,一個網段的最後兩段是1.0和1.255時比較特殊,1.0 表示一個區域的網絡地址-網絡段,1.255表明此網段的廣播地址
UDP傳輸:
UDP發送端:
1.創建UDPSocket服務,經過DatagramSocket對象創建;
2.獲取數據,並將數據封裝到數據包中,數據包對象DatagramPacket;
3.經過Socket服務的發送功能send();方法,將數據包發送出去;
4.關閉資源。
UDP接收端:
1.定義UDPSocket服務,創建DatagramSocket對象時一般會監聽一個端口,其實就是給這個接收網絡應用程序定義數字標識。方便於明確哪些數據過來該應用程序能夠處理;
2.定義一個數據包用來存儲接收到的字節數據。數據包對象中有不少功能能夠提取字節數據中的不一樣數據信息,數據包DatagramPacket對象;
3.經過服務DatagramSocket的receive();方法將接收到的數據存入數據包對象中;
4.在數據包DatagramPacket對象中將這些不一樣的數據取出來。
5.關閉資源。
Socket UDP聊天軟件聊天示例:
1 /** 2 * 模擬聊天軟件,有收數據的部分和發送數據的部分。這兩部分須要同時執行, 3 * 使用多線程技術一個線程控制收,一個線程控制發。 4 * 由於收和發動做不是一致的,因此要定義兩個run方法,而這兩個方法要封裝到不一樣類中 5 */ 6 class Demo 7 { 8 public static void main(String[] args) throws Exception 9 { 10 DatagramSocket send = new DatagramSocket(); 11 DatagramSocket receive = new DatagramSocket(10001); 12 new Thread(new UDPSend(send)).start(); 13 new Thread(new UDPReceive(receive)).start(); 14 } 15 } 16 17 class UDPSend implements Runnable 18 { //發送端 19 private DatagramSocket ds; 20 UDPSend(DatagramSocket ds) 21 { 22 this.ds = ds; 23 } 24 public void run() 25 { 26 try 27 { 28 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 29 String temp; 30 while ((temp = br.readLine()) != null) 31 { 32 if ("exit".equals(temp)) 33 { break;} 34 byte[] line = temp.getBytes(); 35 DatagramPacket dp = new DatagramPacket(line, line.length, InetAddress.getByName("192.168.0.255"), 10001); 36 ds.send(dp); 37 } 38 ds.close(); 39 } 40 catch (Exception e) 41 { 42 e.getStackTrace(); 43 } 44 } 45 } 46 47 class UDPReceive implements Runnable 48 { //接收端 49 private DatagramSocket ds; 50 UDPReceive(DatagramSocket ds) 51 { 52 this.ds = ds; 53 } 54 public void run() 55 { 56 try 57 { 58 while (true) 59 { 60 byte[] line = new byte[1024 * 64]; 61 DatagramPacket dp = new DatagramPacket(line, line.length); 62 ds.receive(dp); 63 String ip = dp.getAddress().getHostAddress(); 64 String data = new String(dp.getData(), 0, dp.getLength()); 65 System.out.println(ip + ": " + data); 66 } 67 } 68 catch (Exception e) 69 { 70 e.getStackTrace(); 71 } 72 } 73 }
TCP傳輸:
1.TCP分客戶端和服務端,分別是獨立的兩個應用程序。
2.客戶端對應的對象是Socket,服務端對應的是ServerSocket,創建鏈接後,經過Socket中的IO流進行數據的傳輸
客戶端:經過Socket對象,創建鏈接指定主機的鏈接。由於TCP是面向鏈接的,因此在創建Socket鏈接時,就要有服務端存在,並確保鏈接成功造成通路後,在該通道進行數據的傳輸。
客戶端創建步驟:
1.建立客戶端Socket服務,並指定要鏈接的主機和端口;
2.準備發送數據,獲取Socket流中的輸出流;
3.關閉資源。
服務端:
服務端創建步驟:
1.創建服務端的Socket服務,ServerSocket(port);並監聽一個端口;
2.獲取鏈接過來的客戶端對象。經過ServerSocket的accept();方法,在沒有鏈接時就會等待,因此這個方法是阻塞式的;
3.客戶端若是發過來數據,那麼服務端要使用對應的客服端對象,並獲取到該客戶端對象的讀取流來讀取發過來的數據;
4.關閉客戶端,服務端通常不關閉(可選)。
TCP文件上傳示例:
1 /** 2 * 實現併發上傳數據 3 * 服務端採用多線程,給每個客戶端訪問鏈接分配一個線程,實現併發多客戶端訪問服務端 4 */ 5 class FileClient 6 { 7 public static void main(String[] agrs) throws Exception 8 { 9 Socket s = new Socket("192.168.0.102", 10000); 10 OutputStream out = s.getOutputStream(); 11 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 12 System.out.println("upload file name or full path:"); 13 //客戶端準備上傳的文件名 14 String fileName = br.readLine(); 15 System.out.println("server file name:"); 16 //上傳文件在服務端名稱 17 String modifyName = br.readLine(); 18 out.write(modifyName.getBytes()); 19 20 FileInputStream fis = new FileInputStream(fileName); 21 byte[] b = new byte[1024]; 22 int len; 23 while ((len = fis.read(b)) != -1) 24 { 25 out.write(b, 0, len); 26 } 27 s.shutdownInput(); 28 s.close(); 29 } 30 } 31 class ServerThread implements Runnable 32 { 33 private Socket s; 34 ServerThread(Socket s) 35 { 36 this.s = s; 37 } 38 @Override 39 public void run() 40 { 41 String ip = s.getInetAddress().getHostAddress(); 42 try 43 { 44 System.out.println(ip + ".....connected success"); 45 byte[] b = new byte[1024]; 46 int len; 47 InputStream in = s.getInputStream(); 48 len = in.read(b); 49 String fileName = new String(b, 0, len); 50 FileOutputStream fos = new FileOutputStream(fileName); 51 byte[] bt=new byte[1024]; 52 while ((len = in.read(bt)) != -1) 53 { 54 fos.write(bt, 0, len); 55 } 56 OutputStream out = s.getOutputStream(); 57 out.write(("上傳成功:" + fileName).getBytes()); 58 fos.close(); 59 s.close(); 60 } 61 catch (Exception e) 62 { 63 System.out.println(ip + "......upload fail"); 64 System.out.println(e.getStackTrace()); 65 } 66 } 67 } 68 class FileServer 69 { 70 public static void main(String[] agrs) throws Exception 71 { 72 ServerSocket ss = new ServerSocket(10000); 73 while (true) 74 { 75 Socket s = ss.accept(); 76 new Thread(new ServerThread(s)).start(); 77 } 78 } 79 }
TCP登陸示例:
1 /** 2 * 客戶端經過鍵盤錄入用戶名和密碼,服務端對這個用戶名和密碼進行校驗 3 * 若是該用戶名存在,而且密碼正確,在服務端顯示XX已登陸,並在客戶端顯示XXX,歡迎光臨 4 * 若是該用戶嘗試三次登陸,用戶名和密碼沒有同時校驗經過,則關閉客戶端 5 */ 6 class LoginClient 7 { 8 public static void main(String[] args) throws Exception 9 { 10 Socket s = new Socket("192.168.0.102", 10000); 11 BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in)); 12 BufferedReader bfw = new BufferedReader(new InputStreamReader(s.getInputStream())); 13 PrintWriter pw = new PrintWriter(s.getOutputStream(), true); 14 15 for (int i = 0; i < 3; i++) 16 { 17 System.out.println("user name:"); 18 String userName = bfr.readLine(); 19 System.out.println("user password:"); 20 String userPwd = bfr.readLine(); 21 pw.println(userName); 22 pw.println(userPwd); 23 String data = bfw.readLine(); 24 String[] result = data.split(":"); 25 if ("true".equals(result[0])) 26 { 27 System.out.println(userName + ",歡迎光臨!"); 28 break; 29 } 30 else 31 { 32 System.out.println(userName + ",登陸失敗! " + result[1]); 33 } 34 } 35 bfr.close(); 36 pw.close(); 37 s.close(); 38 } 39 } 40 class UserThread implements Runnable 41 { 42 private Socket s; 43 UserThread(Socket s) 44 { 45 this.s = s; 46 } 47 @Override 48 public void run() 49 { 50 String ip = s.getInetAddress().getHostAddress(); 51 try 52 { 53 System.out.println(ip + "......connected success"); 54 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); 55 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); 56 57 for (int i = 0; i < 3; i++) 58 { 59 String userName = br.readLine(); 60 String userPwd = br.readLine(); 61 if (userName.isEmpty() || userPwd.isEmpty()) 62 { 63 bw.write("false:The user name or password can not be empty"); 64 bw.newLine(); 65 bw.flush(); 66 System.out.println("user name or password is empty"); 67 continue; 68 } 69 String pwd = getConfigInfo("config.properties", userName); 70 if (!userPwd.equals(pwd)) 71 { 72 bw.write("false:The username or password is wrong"); 73 bw.newLine(); 74 bw.flush(); 75 System.out.println("user password is wrong"); 76 } 77 else 78 { 79 System.out.println("user is Login"); 80 bw.write("true:Login success"); 81 bw.newLine(); 82 bw.flush(); 83 break; 84 } 85 } 86 br.close(); 87 bw.close(); 88 s.close(); 89 System.out.println(ip + "......disconnected"); 90 } 91 catch (Exception e) 92 { 93 System.out.println(ip + "......connected fail"); 94 System.out.println(e.getMessage()); 95 StackTraceElement[] stack = e.getStackTrace(); 96 if (stack != null) 97 { 98 for (int i = 0; i < stack.length; i++) 99 { 100 System.out.println(stack[i].toString()); 101 } 102 } 103 } 104 } 105 106 /** 107 * 獲取配置文件信息 108 * 109 * @param filePath 文件路徑 110 * @param key 鍵值 111 * @return 值 112 */ 113 private String getConfigInfo(String filePath, String key) 114 { 115 Properties properties = new Properties(); 116 try 117 { 118 properties.load(new BufferedReader(new FileReader(filePath))); 119 return properties.getProperty(key); 120 } 121 catch (FileNotFoundException e) 122 { 123 e.printStackTrace(); 124 } 125 catch (IOException e) 126 { 127 e.printStackTrace(); 128 } 129 return null; 130 } 131 } 132 class LoginServer 133 { 134 public static void main(String[] args) throws Exception 135 { 136 ServerSocket ss = new ServerSocket(10000); 137 while (true) 138 { 139 Socket s = ss.accept(); 140 new Thread(new UserThread(s)).start(); 141 } 142 } 143 }
URL類:統一資源定位符
URLConnection openConnection(); //獲取URL 所引用的遠程對象的鏈接
URLConnection conn=url.openConnection();
abstract void connect(); //子類實現方法創建到遠程對象的實際鏈接
OutputStream getOutputStream(); //獲取鏈接讀取的輸入流
InputStream getInputStream(); //獲取鏈接的輸出流
void setConnectTimeout(int timeout); //設置一個指定的超時值(以毫秒爲單位)
void setReadTimeout(int timeout);//將讀超時設置爲指定的超時值,以毫秒爲單位
模擬瀏覽器客戶端:
1 class Demo 2 { 3 public static void main(String[] args) throws Exception 4 { 5 URL url = new URL("http://www.google.cn/"); 6 URLConnection conn = url.openConnection(); 7 conn.connect(); 8 InputStream in = conn.getInputStream(); 9 int len; 10 byte[] bt = new byte[1024*1024]; 11 while ((len = in.read(bt)) != -1) 12 { 13 //注意編碼格式,GBK,UTF-8,Unicode 14 System.out.println(new String(bt, 0, len, "UTF-8")); 15 } 16 } 17 }
25天-01-正則表達式
正則表達式:符合必定規則的表達式,用於專門操做字符串。
特色:用一些特定的符號來表示一些代碼操做,簡化對特殊字符串的校驗
使用正則表達式思路:
1.若是隻想知道該字符串是對是錯,使用匹配;
2.若是想要將已有的字符串變成另外一個字符串,替換;
3.若是想要按照自定的方式將字符串變成多個字符串,切割,獲取規則之外的子串;
4.若是想要拿到符合需求的字符串子串,獲取,獲取符合規則的子串。
正則表達式校驗郵箱地址:
//較爲精確匹配
String reg="[a-zA-Z0-9]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+";
//相對上面,@後面採用模糊匹配,1@1.1會經過校驗
String reg="\\w+@\\w+(\\.\\w+)+";
具體操做功能:用規則匹配整個字符串,只要有一處不符合規則,匹配就結束,返回false
1.匹配方法:boolean matches(String regex); Pattern.matches(regex, str);
Regex示例:
//QQ號匹配:所有爲數字,第一位不爲0,總共有5到15位
String reg="[1-9]\\d{4,14}";
//手機號匹配:13xxx、15xxx、18xxx三個段位,總共11位數字
String reg="1[358]\\d{9}";
2.切割方法:String[] split(String regex); String[] split(String regex, int limit);
Regex示例:
//按照多個空格進行切割
String reg=" +";
//按照.號進行切割
String reg="\\.";
//按照\\進行切割,用於文件路徑操做
String reg="\\\\";
//按照疊詞完成切割。爲了可讓規則的結果被重用,能夠將規則封裝成一個組,用()表示。組的出現都有編號,從1開始,定義編號\\n,想要使用已有的組能夠經過$n(n就是組的編號)的形式來獲取
String reg="(.)\\1+";
3.替換方法:String replace(char oldChar, char newChar); String replace(CharSequence target, CharSequence replacement);
String replaceAll(String regex, String replacement); String replaceFirst(String regex, String replacement);
Regex示例:
//將字符串中的數字替換成*,防止聯繫方式泄露
replaceAll("\\d{5,}","*");
//將重疊的字符替換成單個字母,$1 是獲取前面組的編號.例如:dafaaaffffc ->dafafc
replaceAll("(.)\\1+","$1");
4.獲取方法:將字符串中符合規則的子集取出
步驟:
1.將正則表達式封裝成對象;
2.讓正則對象和要操做的字符串相關聯;
3.關聯後,獲取正則匹配引擎;
4.經過引擎對符合規則的子串進行操做。
獲取單詞示例:
1 import java.util.regex.Matcher; 2 import java.util.regex.Pattern; 3 /** 4 * 其實String類中的matches方法用的就是Pattern和Matcher對象完成的。只不過被String的方法封裝後用起來較爲簡單,可是功能卻單一。 5 */ 6 class Demo 7 { 8 public static void main(String[] args) 9 throws Exception 10 { 11 String str = "ming tian ni shi fou yao jia gei wo!"; 12 String reg = "\\b[a-zA-Z]{2,}\\b"; 13 //將規則封裝成對象 14 Pattern p = Pattern.compile(reg); 15 //讓正則對象和要做用的字符串相關聯,獲取匹配器對象 16 Matcher m = p.matcher(str); 17 //將規則做用到字符串上,並進行符合規則的子集查找,group前必須find 18 while (m.find()) 19 { 20 //用於獲取匹配後的結果 21 System.out.println(m.group()); 22 System.out.println(m.start() + "..." + m.end()); 23 } 24 } 25 }
獲取網頁郵箱示例:
1 import java.io.BufferedReader; 2 import java.io.InputStream; 3 import java.io.InputStreamReader; 4 import java.net.URL; 5 import java.net.URLConnection; 6 import java.util.regex.Matcher; 7 import java.util.regex.Pattern; 8 class Demo 9 { 10 private static final String REGEX_MAIL = "\\w+@\\w+(\\.\\w+)+"; 11 public static void main(String[] args)throws Exception 12 { 13 String dad = "dadaffa@dada.com"; 14 System.out.println(dad.matches(REGEX_MAIL)); 15 16 BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in)); 17 boolean flag = false; 18 while (true) 19 { 20 if (flag) 21 { 22 break; 23 } 24 String str = null; 25 while ((str = bfr.readLine()) != null) 26 { 27 if ("exit".equals(str)) 28 { 29 flag = true; 30 break; 31 } 32 else 33 { 34 try 35 { 36 regexMail(str); 37 } 38 catch (Exception e) 39 { 40 System.out.println("input url is wrong"); 41 System.out.println(e.getMessage()); 42 } 43 } 44 System.out.println("**********************"); 45 System.out.println("please input next url:"); 46 } 47 } 48 System.out.println("program is over!"); 49 } 50 private static void regexMail(String param)throws Exception 51 { 52 Pattern pattern = Pattern.compile(REGEX_MAIL); 53 URL url = new URL(param); 54 URLConnection conn = url.openConnection(); 55 InputStream in = conn.getInputStream(); 56 BufferedReader bfr = new BufferedReader(new InputStreamReader(in, "utf-8")); 57 58 String len = null; 59 while ((len = bfr.readLine()) != null) 60 { 61 Matcher m = pattern.matcher(len); 62 while (m.find()) 63 { 64 System.out.println(m.group()); 65 } 66 } 67 } 68 }
Regex正則表達式規則:
字符
x 字符 x
\\ 反斜線字符
\0n 帶有八進制值 0 的字符 n (0 <= n <= 7)
\0nn 帶有八進制值 0 的字符 nn (0 <= n <= 7)
\0mnn 帶有八進制值 0 的字符 mnn(0 <= m <= 三、0 <= n <= 7)
\xhh 帶有十六進制值 0x 的字符 hh
\uhhhh 帶有十六進制值 0x 的字符 hhhh
\t 製表符 ('\u0009')
\n 新行(換行)符 ('\u000A')
\r 回車符 ('\u000D')
\f 換頁符 ('\u000C')
\a 報警 (bell) 符 ('\u0007')
\e 轉義符 ('\u001B')
\cx 對應於 x 的控制符
字符類
[abc] a、b 或 c(簡單類)
[^abc] 任何字符,除了 a、b 或 c(否認)
[a-zA-Z] a 到 z 或 A 到 Z,兩頭的字母包括在內(範圍)
[a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](並集)
[a-z&&[def]] d、e 或 f(交集)
[a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](減去)
[a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](減去)
預約義字符類
. 匹配除換行符 \n 以外的任何單字符。要匹配 . ,請使用 \.
* 匹配前面的子表達式零次或屢次。要匹配 * 字符,請使用 \*
+ 匹配前面的子表達式一次或屢次。要匹配 + 字符,請使用 \+
[ 標記一箇中括號表達式的開始。要匹配 [,請使用 \[
? 匹配前面的子表達式零次或一次,或指明一個非貪婪限定符。要匹配 ? 字符,請使用 \?
\ 將下一個字符標記爲或特殊字符、或原義字符、或向後引用、或八進制轉義符。例如, 'n' 匹配字符 'n'。'\n' 匹配換行符。序列 '\\' 匹配 "\",而 '\(' 則匹配 "("
^ 匹配輸入字符串的開始位置,除非在方括號表達式中使用,此時它表示不接受該字符集合。要匹配 ^ 字符自己,請使用 \^
{ 標記限定符表達式的開始。要匹配 {,請使用 \{
| 指明兩項之間的一個選擇。要匹配 |,請使用 \|
( ) 標記一個子表達式的開始和結束位置。子表達式能夠獲取供之後使用。要匹配這些字符,請使用 \( 和 \)
邊界匹配器
\A 輸入的開頭
\b 單詞邊界
\B 非單詞邊界
\d 數字:[0-9]
\D 非數字: [^0-9]
\G 上一個匹配的結尾
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 單詞字符:[a-zA-Z_0-9]
\W 非單詞字符:[^\w]
\Z 輸入的結尾,僅用於最後的結束符(若是有的話)
\z 輸入的結尾
Greedy 數量詞 X? X,一次或一次也沒有 X* X,零次或屢次 X+ X,一次或屢次 X{n} X,剛好 n 次 X{n,} X,至少 n 次 X{n,m} X,至少 n 次,可是不超過 m 次