一、String
String为了可以节省存储空间以及避免额外的开销,在需要改变字符串内容时会返回一个新的字符串对象,而如果不需要改变字符串内容,则直接返回原对象的引用。这一点也许也解释了String对象为什么是不可改变的。
String类在实际开发中使用频率非常高,其构造器和常用方法非常多,需要我们在开发中多多使用才能熟悉,具体可以参考如下地址查看:
http://tool.oschina.net/apidocs/apidoc?api=jdk-zh
前几天看了一篇关于JDK9的新特性的博客,介绍了在1.9版本中对String进行了优化,主要是对字符表示进行了一定的调整(即从char——byte),也就是由char数组存放改为byte数组。在1.9之前无论中文还是英文都是采用双字节表示的,但是一个英文字母(包括ISO-8859-1编码内的所有字符)实际只需要一个字节就能进行存储,如果采用俩个字节存储就浪费了。所以在1.9版本后对ISO-8859-1编码内的字符采用1个字节存储,一旦出现编码外的字符就会采用双字节(当然,与编码外以通出现的编码内的字符也同样采用双字节)。所以对于都采用编码内字符开发的项目而言,优化效果是比较明显的,而对于中文而言没有任何变化。
二、类型转换——Formatter(java.util.Formatter)
d——整数型(十进制) | e——浮点数(科学计数) |
c——Unicode字符 | x——正数(十六进制) |
b——Boolean值 | h——散列码(十六进制) |
s——String | %——字符“%” |
f——浮点数(十进制) |
1.Formatter.format()
public class test{
public static void main(String[] args){
Formatter f=new Formatter(System.out);//传入的参数告诉它最终的结果将向哪里输出
int number=180;
f.format("d: %d\n",number);
f.format("c: %c\n",number);
f.format("b: %b\n",number);//只要值不为null,转化结果都为true,即使是0转换结果也为true
f.format("s: %s\n",number);
//f.format("e: %e\n",number);
f.format("x: %x\n",number);
f.format("h: %h\n",number);
}
}
2.String.format()
——静态方法(当只需使用format()方法一次时,推荐使用此方法),内部仍然是先创建一个Formatter,然后将传入的参数转给Formatter。
public class Love{
private String name="li_love_wang";
public static void main(String[] args){
test s=new test();
String finalStr=String.format("%d %s %d forever!",520,s.name,1314);//返回一个String对象
}
}
3.格式的精细化修饰
除了上边的格式转换之外,Formatter还可以进行更加精细复杂的格式修饰,如控制一个域的最大、最小尺寸。Formatter对象会在必要时通过添加空格来确保一个域至少达到某个长度。在默认情况下,数据是右对齐,同时可以通过使用“-”标志来改变对齐方向。
%[对齐方式,默认右对齐,+“-”左对齐][width][.precision][转换类型符]
eg:%15s
eg: %15.15s
- width——指明转换格式的最小尺寸,可以应用于任何的格式转换。
- precision——指明最大尺寸,适用于某些格式。例如,不适合用于整数,会触发异常。用于字符串表示输出字符的最大数量,不够补空格;浮点数,控制小数部分的位数(默认6位),若小数位过多则舍去,不够则补零。
Formatter f=new Formatter(System.out);
f.format("%3s%10s%10s%10s\n","name","sex","age","phone");
f.format("%-3s%10s%10s%10s\n","----","---","---","-----");
f.format("%-13s%-9s%-7s%5s\n","BigTomCat","m","22","13233330521");
f.format("%-13s%-9s%-7s%5.5s\n","Jack","m","24","13233330522");
f.format("%-13s%-9s%-7s%s\n","Rose","w","21","13233330523");
4.Java中格式化的三种形式
//format:"520forever![love you:1314]"
String output="love you";
int love=520;
int time=1314;
//通过字符串拼接
System.out.println(love+"forever!["+output+":"+time+"]");
//通过C中的格式化方法
System.out.printf("%dforever![%s:%d]\n",love,output,time);
//format()是模仿printf()方法创建的
System.out.format("%dforever![%s:%d]\n",love,output,time);
三、正则表达式
1.正则表达式的简单应用
正则表达式提供了一种完全通用的方式,能够解决各种字符串处理相关的问题:匹配、选择、编辑以及验证。Java中与其他语言不同的是用“\\”表示在插入一个正则表达式的反斜杠,如果表示以为数字则需要写为“\\d”,不过对于换行符和制表符和其他语言一样,直接写成“\n”和“\t”即可。简单的正则表达式格式如下所示:
- \\W——非单词字符
- \\w——表示一个单词字符
- n\\W+——表示字母n后面跟着一个或多个非单词字符
- f\\w+——表示f开头的后边跟一个或多个字母
String中含有使用正则表达式处理字符串的方法:
- split([分割规则])——通过指定的分割标准进行字符串的分割,这些标准通常使用正则表达式描述
//format:"520forever![love you:1314]"
String output="love you";
int love=520;
int time=1314;
String str=String.format("%dforever![%s:%d]\n",love,output,time); //通过String.format()进行格式化返回一个String对象
System.out.println(Arrays.toString(str.split("o\\w"))); //返回的String对象调用split()方法进行字符串分割
System.out.println(Arrays.toString(str.split("o"))); //"o"在正则表达式中没有特殊含义,所以不需要进行转义
- replaceFirst([替换条件])——替换字符串中第一个符合此条件的部分
- replaceAll([替换条件])——替换所有符合条件的部分
//format:"520forever![love you:1314]"
String output="love you";
int love=520;
int time=1314;
String str=String.format("%dforever![%s:%d]\n",love,output,time);//格式转换返回String对象
System.out.println(str.replaceFirst("o\\w+","love".toUpperCase()));//首个单词替换
System.out.println(str.replaceAll("o\\w+","love".toUpperCase()));//全部替换
2.创建正则表达式
就和一般的语言一样,要想创建必须要知道其本身的组成元素和语法,下面总结一些正则表达式中常用的元素:
B | 指定字符B(其他字母的含义一样,如A、C等) |
---|---|
\xhh | 十六进制值为oxhh的字符,如:\x12e |
\uhhhh | 十六进制表示为oxhhhh的Unicode字符 |
\t | 制表符Tab |
\n | 换行符 |
\r | 回车 |
\f | 换页 |
\e | 转义 |
[abc] | 包含a,b和c的任何字符(与a|b|c作用相同) |
[^abc] | 除了a,b和c之外的任何字符(否定) |
[a-zA-Z] | 从a到z或从A到Z的任何字符(范围) |
[abc[hij]] | 任意a,b,c,h,i和j字符(合并) |
[a-z&&[hij]] | 任意h,i或j(交) |
\s | 表示所有的空白符(空格,tab,换行,换页和回车) |
\S | 非空白符([^\s]) |
\d | 数字[0-9] |
\D | 非数字[^0-9] |
\w | 词字符[a-zA-Z0-9] |
\W | 非词字符[^\w] |
XY | Y跟在X后面,如ab |
X|Y | X或Y |
(X) | 捕捉组。可以在表达式中用“\i”引用第i个捕获组 |
^ | 一行的起始 |
\B | 非词的边界 |
$ | 一行的结束 |
\G | 前一个匹配的结束 |
\b | 词的边界 |
贪婪型 | 勉强型 | 占有型 | 如何匹配 |
X? | X?? | X?+ | 一个或零个X |
X* | X*? | X*+ | 零个或多个X |
X+ | X+? | X++ | 一个或多个X |
X{n} | X{n}? | X{n}+ | 恰好n次X |
X{n,} | X{n,}? | X{n,}+ | 至少n次X |
X{n,m} | X{n,m}? | X{n,m}+ | X至少n次,但不能超过m次 |
何为量词(字符串匹配中常用的工具):
量词描述了一个模式吸收输入文本的方式。
- 贪婪型(最大匹配):——Java中的默认匹配类型
会为所有可能的模式发现尽可能多个匹配。也就是当匹配到一个目标后会继续向下进行下去。
- 勉强型(最小匹配):
用问号来指定,满足模式所需的最少字符串个数,因此被称作懒惰的、非贪婪的或不贪婪的。(添加?)
- 占有型(完全匹配):
目前只在Java存在,为更高级的一种模式。因”占有的“量词并不保存这些中间状态,因此它们可以防止回溯。它们常常用于防止正则表达式失控,因此可以使正则表达式执行起来更有效。与贪婪型不同的是,贪婪型在一次匹配失败后会回溯回去然后缩小范围进行重新匹配,但占有型由于没有回溯,所以当一遍匹配失败后就不做匹配了。(添加+)
eg:
String strM="java<hello>this<world>";
//贪婪型
String reg1="<.+>";
//勉强型
String reg2="<.+?>";
//占有型
String reg3="<.++>";
System.out.println(strM.replaceAll(reg1,"***"));
System.out.println(strM.replaceAll(reg2,"***"));
System.out.println(strM.replaceAll(reg3,"***"));
可以很容易从这个例子看出三种模式的区别。
注:
abc+——代表ab后面跟随一个或多个c
(abc)+——代表匹配一个或多个abc
3. 正则表达式的使用
3.1静态字符序列中的应用
import java.util.regex.*; //第一步先导入匹配包(使用Pattern和Matcher俩个类)
System.out.println("--------Pattern-----------");
String words="Arline ate eight apples and one orange while Anita hadn`t any"; //需要匹配的字符串
Pattern p=Pattern.compile("a\\w+(\\S+)\\s"); //通过compile([正则表达式])获取Pattern对象
Matcher m=p.matcher(words); //通过matcher()获的Matcher对象
while(m.find()){
System.out.print(m.group()+":"+m.start()+"---"+(m.end()-1)+"\n");
}
System.out.println("groupCount():"+m.groupCount());
//System.out.print("group(int i):"+m.group(1));
System.out.println("find():"+m.find());
System.out.println("find(int i):"+m.find(1));
System.out.println("lookingAt():"+m.lookingAt());
System.out.println("matches():"+m.matches());
System.out.println("\n----------------------------");
例子中使用了常用的一些方法:
- Pattern Pattern.compile(Stirng str)——获取Pattern对象
- Matcher Pattern.compile(Stirng str).matcher(String str,int flag)——获取Matcher对象
*记”m“为Matcher的对象引用:
这些方法中涉及到一个组——组是用括号划分的正则表达式,可以根据组的编号来引用某个组(如:group(1)),组号为0表示整个表达式,组号为1表示被第一个括号括起来的组,其他以此类推。
A(B(C))D——此为3个组组成:组0是ABCD,组1是BC,组2是C。
- int m.groupCount()——获取组数
- String m.group(int i)——返回指定组捕获的匹配的子序列
- String m.group()——返回已匹配的字符序列
- int m.start()——获取当前匹配序列的开始位置
- int m.end()——获取当前匹配序列的结束位置+1
- boolean m.find()——是否匹配成功
- boolean m.find(int i)——指定位置是否匹配成功
- boolean m.lookingAt()——匹配的字符序列的开头是否匹配成功
- boolean m.matches()——判断输入的整个字符串是否匹配成功
*记”p“为Pattern的对象引用:
分割:Stirng[] p.split(CharSequence input,int limit)——按照指定断开边界将字符系列input进行分割并返回一个字符串数组,int limit是一个可有可无的参数,当添加limit时指定到第几个位置处停止分割。
System.out.println("\n----------------------------");
String[] svector=Pattern.compile("!!").split("JAVA!!Android!!J2EE!!JAVASE"); //无limit
for(String subs:svector){
System.out.println(subs);
}
System.out.println("----------");
String[] svector2=Pattern.compile("!!").split("JAVA!!Android!!J2EE!!JAVASE",3); //有limit
for(String subs:svector2){
System.out.println(subs);
}
System.out.println("\n----------------------------");
替换:
- m.replaceFirst(String replacement)——替换第一个匹配成功的部分
- m.replaceAll(Stirng replacement)——替换所有匹配成功的部分
- m.appendReplacement(StringBuffer sbuf,String replacement)——执行渐进式的替换
- m.appendTail(StringBuffer sbuf)——在执行了一次或多次appendReplacement()后,将输入字符串余下部分复制到sbuf中
- m.reset(String squence)——可以将现有的Matsher对象应用于一个新的字符序列
System.out.println("\n----------------------------");
String neww=words.replaceFirst("[aeiou]","***");
System.out.println(neww);
String neww2=words.replaceAll("[aeiou]","*");
System.out.println(neww2);
StringBuffer sub=new StringBuffer();
Matcher mtc=Pattern.compile("[aeiou]").matcher(words);
while(mtc.find()){
mtc.appendReplacement(sub,mtc.group().toUpperCase());
}
System.out.println(sub);
mtc.appendTail(sub); //可将替换后剩余的字符保存到StringBuffer中
System.out.println(sub);
mtc.reset("hello world!"); //将之前需要替换的目标字符序列进行替换
while(mtc.find()){
System.out.println(mtc.group());
}
System.out.println("\n----------------------------");
注:
- 由于replaceFirst()和replaceAll()俩个方法在String中同样存在,所以直接使用String中的方法,这样由于不需要创建Matcher对象而节省了开销。
- 为什么说appendReplacement()更强大呢,因为其在在替换中可以对要替换的目标进行特殊处理,如上边例子中将需要被替换的字符进行大写转换。
- 当reset()中不添加参数时,可以将Matcher对象重新设置到当前字符序列的起始位置。
3.2数据扫描
当我们需要字符序列中的一些数字或者其他类型的数据进行操作时,最好的办法就是将我们需要的数据进行提取然后应用。JavaSE5之后可以通过使用Scanner类进行字符序列的扫描。
System.out.println("\n----------------------------");
Scanner sca=new Scanner("this is a book \n5 22.5"); //Scanner类可以接收任何类型的输入对象,包括File对象、InputStream、String等
System.out.println("say words:"+sca.nextLine());
System.out.println("the number of ticket:"+sca.nextInt());
System.out.println("the price of one ticket:"+sca.nextDouble());
System.out.println("\n----------------------------");
从运行结果可以看出Scanner可以自动的进行分词和翻译,并通过其一系列的next方法获取不同类型的数据。
当然,Scanner也可以设定我们自己的界定符来对数据进行分词:
System.out.println("\n----------------------------");
sca=new Scanner("12-- 24-- 48-- 96");
sca.useDelimiter("\\s*--\\s*");//此界定符表示在"--"前后有零个或多个空白符
while(sca.hasNextInt()){
System.out.println(sca.nextInt());
}
System.out.println("----------------------------");
可以通过useDelimiter([正则表达式])来作为Scanner的界定符。
用正则表达式进行扫描:
import java.util.*;
import java.util.regex.*;
public class regex{
public static void main(String[] args) throws Exception{
String text=
"101.10.10.01@23/05/2018\n"+
"101.10.10.02@23/05/2018\n"+
"101.10.10.03@23/05/2018\n"+
"101.10.10.04@23/05/2018\n"+
"[this is a login log]" ;
Scanner sc=new Scanner(text);
String pattern="(\\d+[.]\\d+[.]\\d+[.]\\d+)+@+(\\d{2}/\\d{2}/\\d{4})";
while(sc.hasNext(pattern)){
sc.next(pattern);
MatchResult result=sc.match();
String ip=result.group(1);
String time=result.group(2);
System.out.format("Threat on %s from ip:%s\n",time,ip);
}
}
}
3.3正则表达式与文件
之前所有的操作都是通过正则表达式操作静态字符序列,那么我们如何将正则表达式应用到一个文件中进行搜索匹配呢?其实与静态字符串是一样的,我们可以先将文件中的数据读出来返回为String类型的或者其他数据流,然后循环通过Pattern和Matcher类进行匹配即可。
3.4正则表达式中的模式
在这些模式中,最常用的就是Pattern.CAZE_INSENSITIVE(?i)、Pattern.MULTILINE(?m)以及Pattern.COMMENTS(?x)(对声明和文档有用)。
实例:
System.out.println("----------------------------");
//Pattern pp=Pattern.compile("^java",Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);
Pattern pp=Pattern.compile("(?i)(?m)^java");
//Pattern pp=Pattern.compile("(?i)^java");
//Pattern pp=Pattern.compile("(?m)^java");
Matcher mm=pp.matcher("java is a very good computer language\nJava has String \n"+
"Java has regex\njava has class\n"+
"I like it,if you do,we to lean Java together!");
while(mm.find()){
System.out.println(mm.group());
}
System.out.println("----------------------------");