网络编程、正则表达式
网络编程
1 概述
Java是 Internet 上的语言,它从语言级上提供了对网络应用程 序的支持,程序员能够很容易开发常见的网络应用程序。 Java提供的网络类库,可以实现无痛的网络连接,联网的底层 细节被隐藏在 Java 的本机安装系统里,由 JVM 进行控制。并 且 Java 实现了一个跨平台的网络库,程序员面对的是一个统一 的网络编程环境。
2 网络通信
UDP和TCP区别
- TCP协议:
? 使用TCP协议前,须先建立TCP连接,形成传输数据通道 ? 传输前,采用“三次握手”方式,点对点通信,是可靠的 ? TCP协议进行通信的两个应用进程:客户端、服务端。 ? 在连接中可进行大数据量的传输 ? 传输完毕,需释放已建立的连接,效率低 - UDP协议:
? 将数据、源、目的封装成数据包,不需要建立连接 ? 每个数据报的大小限制在64K内 ? 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的 ? 可以广播发送 ? 发送数据结束时无需释放资源,开销小,速度快
通信双方地址 a)IP b)端口号 一定的规则(即:网络通信协议。有两套参考模型) c)OSI参考模型:模型过于理想化,未能在因特网上进行广泛推广 d)TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。 2.1.通信要素1:IP地址 IP 地址:InetAddress 唯一的标识 Internet 上的计算机(通信实体) 本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost IP地址分类方式1:IPV4 和 IPV6 IPV4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已经用尽。以点分十进制表示,如192.168.0.1 IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示, 数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984 IP地址分类方式2:公网地址(万维网使用)和私有地址(局域网使用)。192.168. 开头的就是私有址址,范围即为192.168.0.0–192.168.255.255,专门为组织机 构内部使用 特点:不易记忆 端口号标识正在计算机上的进程 规定为16位整数 端口号与IP地址组合得出一个网络套接字:Socket
TCP
tcp三次握手:建立tcp连接,客户端和服务端之间客户端先发,两者交替总共发送三个包确立链接. TCP四次挥手结束连接参考https://www.jianshu.com/p/ef892323e68f
Socket
利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实 上的标准。 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标 识符套接字。 通信的两端都要有Socket,是两台机器间通信的端点。 网络通信其实就是Socket间的通信。 Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。 Socket分类: 流套接字(stream socket):使用TCP提供可依赖的字节流服务数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务 常用方法 Socket类的常用构造器: public Socket(InetAddress address,int port)创建一个流套接字并将其连接到指定IP 地址的指定端口号。 public Socket(String host,int port)创建一个流套接字并将其连接到指定主机上的指定端口号。 Socket类的常用方法: public InputStream getInputStream()返回此套接字的输入流。可以用于接收网络消息 public OutputStream getOutputStream()返回此套接字的输出流。可以用于发送网络消息 public InetAddress getInetAddress()此套接字连接到的远程 IP 地址;如果套接字是未连接的,则返回 null。 public InetAddress getLocalAddress()获取套接字绑定的本地地址。 即本端的IP地址 public int getPort()此套接字连接到的远程端口号;如果尚未连接套接字,则返回 0。 public int getLocalPort()返回此套接字绑定到的本地端口。 如果尚未绑定套接字,则返回 -1。即本端的 端口号。 public void close()关闭此套接字。套接字被关闭后,便不可在以后的网络连接中使用(即无法重新连接 或重新绑定)。需要创建新的套接字对象。 关闭此套接字也将会关闭该套接字的 InputStream 和 OutputStream。 public void shutdownInput()如果在套接字上调用 shutdownInput() 后从套接字输入流读取内容,则流将 返回EOF(文件结束符)。 即不能在从此套接字的输入流中接收任何数据。 public void shutdownOutput()禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发 送,并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流, 则该流将抛出 IOException。 即不能通过此套接字的输出流发送任何数据。 服务端
public static void testMany() throws Exception{
ServerSocket ss = new ServerSocket(9999);
System.out.println("服务端已启动,正在等在连接.....");
Socket skt = ss.accept();
System.out.println("客户端已连接");
TcpManager.socket(skt);
}
客户端
public static void main(String[] args) throws Exception {
Socket skt = new Socket("127.0.0.1",9999);
System.out.println("连接成功");
TcpManager.socket(skt);
}
业务类(多线程阻塞式交互)
public class TcpManager {
public static void socket(Socket skt) throws Exception {
OutputStream os = skt.getOutputStream();
PrintWriter out = new PrintWriter(new OutputStreamWriter(os, "utf-8"));
InputStream is = skt.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is,
"utf-8"));
Thread outThread = new Thread(new Runnable() {
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
String msg = null;
while ((msg = scanner.nextLine()) != null && !msg.equals("拜拜")) {
out.println(msg);
out.flush();
}
System.out.println("聊天结束");
}
});
outThread.start();
Thread intThread = new Thread(new Runnable() {
@Override
public void run() {
String msg = null;
try {
while ((msg = br.readLine()) != null && !msg.equals("拜拜")) {
System.out.println(skt.getInetAddress().getHostName()
+ " : " + msg);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("聊天结束");
}
});
intThread.start();
outThread.join();
br.close();
is.close();
out.close();
os.close();
skt.close();
}
}
UDP
- DatagramSocket与DatagramPacket
- 建立发送端,接收端
- 建立数据包
- 调用Socket的发送、接收方法
- 关闭Socket
发送端与接收端是两个独立的运行程序
DatagramSocket 类的常用方法 public DatagramSocket(int port)创建数据报套接字并将其绑定到本地主机上的指定端口。套接字将被绑定到通配符地址,IP 地址由内核来选择。 public DatagramSocket(int port,InetAddress laddr)创建数据报套接字,将其绑定到指定的本地地址。 本地端口必须在 0 到 65535 之间(包括两者)。如果 IP 地址为 0.0.0.0,套接字将被绑定到通配符地 址,IP 地址由内核选择。 public void close()关闭此数据报套接字。 public void send(DatagramPacket p)从此套接字发送数据报包。DatagramPacket 包含的信息指示:将要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号。 public void receive(DatagramPacket p)从此套接字接收数据报包。当此方法返回时,DatagramPacket 的缓冲区填充了接收的数据。数据报包也包含发送方的 IP 地址和发送方机器上的端口号。 此方法 在接收到数据报前一直阻塞。数据报包对象的 length 字段包含所接收信息的长度。如果信息比包的 长度长,该信息将被截短。 public InetAddress getLocalAddress()获取套接字绑定的本地地址。 public int getLocalPort()返回此套接字绑定的本地主机上的端口号。 public InetAddress getInetAddress()返回此套接字连接的地址。如果套接字未连接,则返回null。 public int getPort()返回此套接字的端口。如果套接字未连接,则返回 -1。 DatagramPacket类的常用方法 public DatagramPacket(byte[] buf,int length)构造 DatagramPacket,用来接收长 度为length 的数据包。 length 参数必须小于等于 buf.length。 public DatagramPacket(byte[] buf,int length,InetAddress address,int port)构造数 据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。length 参数必须小于等于buf.length。 public InetAddress getAddress()返回某台机器的 IP 地址,此数据报将要发往该 机器或者是从该机器接收到的。 public int getPort()返回某台远程主机的端口号,此数据报将要发往该主机或 者是从该主机接收到的。 public byte[] getData()返回数据缓冲区。接收到的或将要发送的数据从缓冲区 中的偏移量 offset 处开始,持续length 长度。 public int getLength()返回将要发送或接收到的数据的长度。 服务端
public class UDPServer {
public static void main(String[] args) throws Exception{
DatagramSocket ds = new DatagramSocket(9999);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
while (true) {
ds.receive(dp);
ByteArrayInputStream bais = new ByteArrayInputStream(buf);
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readUTF());
}
}
}
客户端
public class UDPClient {
public static void main(String[] args) throws Exception {
String string = "你好吗";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeUTF(string);
byte[] buf = baos.toByteArray();
DatagramPacket dp = new DatagramPacket(buf, buf.length,new InetSocketAddress("127.0.0.1", 9999));
DatagramSocket ds = new DatagramSocket(9998);
ds.send(dp);
}
}
正则表达式
-
总览 正则表达式 : 定义了字符串的匹配模式 可以用来搜索,编辑或处理文本,并不仅限于某一种语言,但是每种语言中有细微的差别 JDK1.4 推出的 java.util.regex 包,很好的支持了正则表达式
-
字符取值范围相关 [abc] : 表示 可能是a, 也可能是b, 也可能是c (是abc中的任意一个) [^abc] : 表示不是a,b,c中的任何一个 [a-zA-Z] : 表示是大小写字母 , [a-z] 小写字母 , [A-Z] 大写字母 [0-9] 数字0-9 [a-zA-Z0-9] : 表示是数字或字母 -
简洁表示 . : 匹配任意字符 \d : 表示数字 等同于[0-9] \D : 表示非数字 等同于 [^0-9] \s : 表示由空字符组成, [\t\n\r\x\f] \S : 表示非空字符组成 \w : 表示由字母数字下划线组成 [a-zA-Z0-9_] \W : 表示非字母数字下划线 [^a-zA-Z0-9_] -
表示数量的 ? : 表示出现0次或1次 + : 表示1次或多次, 大于等于1 * : 任意次数(0~N) {n} : 表示出现n次 , {2} 表示出现2次 {n,m} : 表示出现n次到m次 , {2,5} : 表示出现2~5次 {n,} : 表示出现n及以上 >=n () : 把他们看做一个整体
| 或 , a|b ,a和b都可以匹配
^ : 以什么什么打头 , 但是 用在[^xxxx] 就表示取反 $ : 以什么什么结尾 ^ a[^a-z] , a1 , aA , a3, 22
语法
元字符 | 描述 |
---|
\ | 将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如,“\n”匹配\n。“\n”匹配换行符。序列“\”匹配“\”而“(”则匹配“(”。即相当于多种编程语言中都有的“转义字符”的概念。 | ^ | 匹配输入字符串的开始位置。如果设置了RegExp对象的属性,^也匹配“\n”或“\r”之后的位置。 | $ | 匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属\性,$也匹配“\n”或“\r”之前的位置。 | * | 匹配前面的子表达式任意次。例如,zo*能匹配“z”,“zo”以及“zoo”。*等价于{0,}。 | + | 匹配前面的子表达式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。 | ? | 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等价于{0,1}。 | {n} | n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。 | {n,} | n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。 | {n,m} | m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。 | . | 点匹配除“\r\n”之外的任何单个字符。要匹配包括“\r\n”在内的任何字符,请使用像“[\s\S]”的模式。 | (pattern) | 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“(”或“)”。 | (?:pattern) | 匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。 | (?=pattern) | 正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 | (?!pattern) | 正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。 | (?=pattern) | 反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。 | (?<!pattern) | 反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。 | x | y | 匹配x或y。例如,“z|food”能匹配“z”或“food”或"zood"(此处请谨慎)。“(z|f)ood”则匹配“zood”或“food”。 | [xyz] | 字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。 | [^xyz] | 负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”。 | [a-z] | 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身. | [^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。 | \b | 匹配一个单词边界,也就是指单词和空格间的位置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。 | \B | 匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。 | \cx | 匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。 | \d | 匹配一个数字字符。等价于[0-9]。 | \D | 匹配一个非数字字符。等价于[^0-9]。 | \f | 匹配一个换页符。等价于\x0c和\cL。 | \n | 匹配一个换行符。等价于\x0a和\cJ。 | \r | 匹配一个回车符。等价于\x0d和\cM。 | \s | 匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。 | \S | 匹配任何可见字符。等价于[^ \f\n\r\t\v]。 | \t | 匹配一个制表符。等价于\x09和\cI。 | \v | 匹配一个垂直制表符。等价于\x0b和\cK。 | \w | 匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集。 | \W | 匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。 | \xn | 匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。 | \num | 匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。 | \n | 标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。 | \nm | 标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。 | \nml | 如果n为八进制数字(0-7),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。 | \un | 匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(?)。 | \ < \ > | 匹配词(word)的开始(\ <)和结束(\ >)。例如正则表达式\ < the \ >能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。 | \ ( \ ) | 将 \ ( 和 \ ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。 | | | 将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him | + | 匹配1或多个正好在它之前的那个字符。例如正则表达式9+匹配9、99、999等。注意:这个元字符不是所有的软件都支持的。 | ? | 匹配0或1个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的。 | {i} {i,j} | 匹配指定数目的字符,这些字符是在它之前的表达式定义的。例如正则表达式A[0-9]{3} 能够匹配字符"A"后面跟着正好3个数字字符的串,例如A123、A348等,但是不匹配A1234。而正则表达式[0-9]{4,6} 匹配连续的任意4个、5个或者6个数字 |
1 匹配整数和小数 (-)?\\d+(\\.\\d+)?
2 电话校验 \\d{11} 1[345789]\\d{9}
Pattern
Pattern : 创建正则表达式对象,能做一些基本的简单操作
- 三大功能 :
验证 : boolean matchers(String regex); 拆分 : String[] split(String regex); 替换 : String replaceAll(String regex,String replacement); 实际操作中,有时候我们嫌麻烦,也可以直接使用String里面的一些方法,比如 替换,拆分,验证String类中也有的
public class _01_Pattern {
public static void main(String[] args) {
test_02();
}
public static void test_02() {
String regex = "(-)?\\d+(\\.\\d+)?";
String str = "2.43";
boolean flag = Pattern.matches(regex, str);
System.out.println(flag);
System.out.println(str.matches(regex));
}
public static void test_01(){
String str = "1,2@3!4";
Pattern pattern = Pattern.compile("[^0-9]");
String[] strArray = pattern.split(str);
for (String string : strArray) {
System.out.println(string);
}
String[] strArray1 = str.split("[^0-9]");
for (String string : strArray1) {
System.out.println(string);
}
}
}
Matcher
构造方法也是私有的,不能随意创建,只能通过Pattern.matcher(CharSequence input) 方法得到该类的实例 Matcher m = p.matcher("aaaaab"); 它支持便捷强大的正则匹配操作,包括分组、多次匹配支持
Matcher.matches(): 对整个字符串进行匹配,只有整个字符串都匹配了才返回true Matcher.lookingAt(): 对前面的字符串进行匹配,只有匹配到的字符串在最前面才返回true Matcher.find(): 对字符串进行匹配,匹配到的字符串可以在任何位置
字符串匹配
public class _02_Matcher {
public static void main(String[] args) {
String regex = "\\d{11}";
String tel = "a131131131112";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(tel);
System.out.println(matcher.matches());
matcher = pattern.matcher(tel);
System.out.println(matcher.lookingAt());
matcher = pattern.matcher(tel);
System.out.println(matcher.find());
}
}
数据提取
public class _03_Matcher_02 {
public static void main(String[] args) {
String string = "张三电话号码是13113113111张三四电话号码是13113113112张三五电话号码是13113113113张四电话号码是13113113114王五电话号码是13113113115赵六电话号码是13113113333孙小黑电话号码是13113113211小红电话号码是13113113311";
String regex = "((.{2,3})电话号码是)(\\d{11})";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(string);
while (matcher.find()) {
System.out.println(matcher.group(2) + " : " + matcher.group(3));
}
}
}
叠词去重
public class _04_Test {
public static void main(String[] args) {
String str = "我我我、、、我我、、我、我要要、、、要要要、、要要、、学学学、、、、学学、、、学编编编、、编编编程、、程程";
str = str.replaceAll("[^\u4e00-\u9fa5]", "");
String regex = "(.)(\\1+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
System.out.println(matcher.group());
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
}
str = str.replaceAll(regex, "$1");
System.out.println(str);
}
}
|