思维导图
一.String类
String类是Java中用来表示字符串的类,有一些有趣的特性.
- 1.String对象是不可变的,也就是说,对String的变动其实返回的都是一个新的String对象,比如string+string.
//测试String的不变特性
public void test1(){
String s1 = "ceshi";
String s2 = s1.toUpperCase();
//有这个输出可以看到,s1没有改变
System.out.println(s1);
System.out.println(s2);
}
- 2.Java中的+和+=对String类做了重载,表示连接两个字符串
- 3.当对象重写自己的toString方法时,如果用string+this将会导致无意识的递归.
//测试无意识的递归
public void test3(){
class TestA{
@Override
public String toString() {
//会将this自动转化为调用其toString方法,从而导致递归
return "TestA{}" + this;
}
}
}
字符串还有一系列功能相当强大的方法,这里不一一列举,后文中会提到一些关于正则表达式的方法.
二.格式化输出
格式化输出的语法:
- %[-][index$][flags][width][.precision]conversion
- 默认为右对齐,有了'-'后,变为左对齐
- index$可以指定输入数据的位置,比如:String.format("我叫%2$s,她叫%1$s", "小明","小方");会输出我叫小方,她叫小明
- flags则是一些特殊标记,比如:','表示金额用千分位分开,0表示结果用0填充
- width表示数据的最小尺寸
- .precision则表示精度,也可以表示最大尺寸,只用于String和浮点数
- conversion表示占位符,表示后方输入数据的类型,比如:s-字符串,d-整数,c-unicode字符,f-浮点数,e-科学计数法,x-16进制整 数,h-散列码,b-布尔类型,可以进行数据类型的转换
怎样调用格式化输出的方法
Java中为格式化输出准备了一个工具类,叫Formtter,用于进行格式化输出,创建时需要指定输出到哪
//用Formatter类测试格式化输出
public void testFormat(){
//创建Formatter要指定输出到哪
Formatter formatter = new Formatter(System.out);
formatter.format("test1:%d\n",1);
//'-'表示左排列,默认是右排列,所以输出中hello和lalala都不会和----挨在一起
formatter.format("test2:%-10.20s----%10.20s","hello","lalala");
}
除此之外,Stirng.format()方法,和PrintStream的printf()/format()方法都可以进行格式化输出,但是这些方法本质上都是调用Formatte实现的.
PS:System.out是标准IO,是一个PrintStream,所以可以调用printf()/format()方法.
三.正则表达式
正则表达式是一个可以对文本进行高效处理的工具,在Java中,他比较特殊,即用\\代替了其他语言中的\表示正则表达式的反斜线,普通的字符'\''则用'\\\\'来表示
3.1正则表达式的语法:
x | 字符 x |
\\\\ | 反斜线字符 |
\0n | 带有八进制值 0 的字符 n (0 <= n <= 7) |
\0nn | 带有八进制值 0 的字符 nn (0 <= n <= 7) |
\0mnn | 带有八进制值 0 的字符 mnn(0 <= m <= 3、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(交集) |
. | 任何字符(与行结束符可能匹配也可能不匹配) |
\d | 数字:[0-9] |
\D | 非数字: [^0-9] |
\s | 空白字符:[ \t\n\x0B\f\r] |
\S | 非空白字符:[^\s] |
\w | 单词字符:[a-zA-Z_0-9] |
\W | 非单词字符:[^\w] |
XY | X 后跟 Y |
X|Y | X 或 Y |
(X) | X,作为捕获组 |
这里需要对组的概念进行说明,正则表达式匹配的的字符串整体是group(0),而如果正则表达式中有用()分组,那么组的排列顺序从左到右计算,用group(i)获得.看下例:
//测试组的分配和获得
public void testGroup(){
String testString = "hello,my regex";
//在这个正则表达式中,group(0)是整体上被匹配的,也就是testString,
//group(1)是','group(2)是mygroup(3)则是my后的所有字符
Pattern p = Pattern.compile("h\\w+(,)(my)(.+)");
Matcher m = p.matcher(testString);
while(m.find()){
System.out.println(m.group(0));
System.out.println(m.group(1));
System.out.println(m.group(2));
System.out.println(m.group(3));
}
}
/*out
hello,my regex
,
my
regex
*/
^ | 行的开头 |
$ | 行的结尾 |
\b | 单词边界 |
\B | 非单词边界 |
\A | 输入的开头 |
\Z | 输入的结尾,仅用于最后的结束符(如果有的话) |
\z | 输入的结尾 |
\G | 上一个匹配的结尾 |
量词分为贪婪型,勉强型,占有型,勉强型会在贪婪型的基础上加一个?,占有型则是加一个+.
X? | X,一次或一次也没有 |
X* | X,零次或多次 |
X+ | X,一次或多次 |
X{n} | X,恰好 n 次 |
X{n,} | 至少 n 次 |
X{n,m} | X,至少 n 次,但是不超过 m 次 |
贪婪型,勉强型,占有型量词的区别举例:
- 贪婪型 :java正则表达式默认匹配是贪婪型,就是原始表达式(x?),会 为所有可能的模式发现尽可能多的匹配。
-
Matcher m = Pattern.compile("x+").matcher("xxxxxx"); if(m.find()){ System.out.println(m.group()); } /*Oputput: xxxxxx */
- 勉强型 :原始表达式后边加个?号(x+?),会匹配满足模式所需的最少字符数。
-
Matcher m = Pattern.compile("x+?").matcher("xxxxxx"); if(m.find()){ System.out.println(m.group()); } /*Output: x */
- 占有型 :原始表达式后边加个+号(x++), 当正则表达式被应用于字符串时,他会产生相当多的状态,以便匹配失败时可以回 溯。而“占有型”量词不会保存这些中间状态,因此他们可以防止回溯。他们常常用于防止正则表达式失败,因此可 以使用正则表达式起来更加有效。
-
//测试占有型量词 public void test99(){ // \\d+都匹配到了3 6 0 3,遇到空格就无法匹配,导致后面的\\d就无法匹配,他就进行回溯,最终让后一个\\d匹配3 6 0 3 Matcher m = Pattern.compile("\\d+\\d").matcher("123 456 78910 111213"); while(m.find()){ System.out.println(m.group()); } System.out.println("----------------------------------------"); // 但是占有型则不回溯,一个也匹配不成 Matcher m2 = Pattern.compile("\\d++\\d").matcher("123 456 78910 111213"); while(m2.find()) System.out.println(m2.group()); } /*Output: 123 456 78910 111213 ---------------------------------------- */
标记模式 CANON_EQ
启用规范等价。
指定此标志后,当且仅当其完整规范分解匹配时,两个字符才可视为匹配。例如,当指定此标志时,表达式 "a\u030A" 将与字符串 "\u00E5" 匹配。默认情况下,匹配不考虑采用规范等价。
不存在可以启用规范等价的嵌入式标志字符。
CASE_INSENSITIVE(?i)
启用不区分大小写的匹配。默认情况下,不区分大小写的匹配假定仅匹配 US-ASCII 字符集中的字符。可以通过指定
UNICODE_CASE
标志连同此标志来启用 Unicode 感知的、不区分大小写的匹配COMMENTS (?x)
模式中允许空白和注释。
此模式将忽略空白和在结束行之前以 # 开头的嵌入式注释。
通过嵌入式标志表达式 (?x) 也可以启用注释模式。
DOTALL(?s)
启用 dotall 模式。
在 dotall 模式中,表达式 . 可以匹配任何字符,包括行结束符。默认情况下,此表达式不匹配行结束符。
通过嵌入式标志表达式 (?s) 也可以启用 dotall 模式(s 是 "single-line" 模式的助记符,在 Perl 中也使用它)。
LITERAL
启用模式的字面值解析。
指定此标志后,指定模式的输入字符串就会作为字面值字符序列来对待。输入序列中的元字符或转义序列不具有任何特殊意义。
标志 CASE_INSENSITIVE 和 UNICODE_CASE 在与此标志一起使用时将对匹配产生影响。其他标志都变得多余了。
MULTILINE(?m)
启用多行模式。
在多行模式中,表达式 ^ 和 $ 仅分别在行结束符前后匹配,或者在输入序列的结尾处匹配。默认情况下,这些表达式仅在整个输入序列的开头和结尾处匹配。
通过嵌入式标志表达式 (?m) 也可以启用多行模式。
UNICODE_CASE (?u)
启用 Unicode 感知的大小写折叠。
指定此标志后,由
CASE_INSENSITIVE
标志启用时,不区分大小写的匹配将以符合 Unicode Standard 的方式完成。默认情况下,不区分大小写的匹配假定仅匹配 US-ASCII 字符集中的字符。通过嵌入式标志表达式 (?u) 也可以启用 Unicode 感知的大小写折叠。
使用举例:
//这两个是一样的
Pattern p = Pattern.compile("h\\w+(,)(my)(.+)",Pattern.MULTILINE |
Pattern.CASE_INSENSITIVE);
Pattern p = Pattern.compile("(?m)(?i)h\\w+(,)(my)(.+)");
3.2正则表达式的调用
在写语法的时候已经简单的使用了matcher的一部分方法,他还有更多的实用方法.
//测试Matcher的各种方法
public void testMatcher(){
String s = "nigoule xiaohao\n"+
"nihao wobugou";
//匹配以n开头,内部含有元音字母的单词
Pattern pattern = Pattern.compile("\\bn(\\w*)[aeiou](\\w*)\\b");//贪婪型
Matcher matcher = pattern.matcher(s);
//使用find()以发现匹配的部分
while(matcher.find()){
//测试group()方法和start(),end()
System.out.println("group:" + matcher.group());
System.out.println("start():" + matcher.start());
System.out.println("start():" + matcher.end());
//group() 等同于 group(0)
System.out.println("group(0):" + matcher.group(0));
System.out.println("group(1):" + matcher.group(1));
System.out.println("start():" + matcher.start(1));
System.out.println("start():" + matcher.end(1));
//贪婪模式会匹配到最后一个元音字母,所以group(2)是空的
System.out.println("group(2):" + matcher.group(2));
//匹配到字符串后的修改使用appendReplacement()
//必须有StringBuffer,以储存修改后的字符串
StringBuffer sb = new StringBuffer();
matcher.appendReplacement(sb,"XXXXX");
//将匹配后的字符串输出
//在while循环内只会将本次匹配的字符串替换,如果在while调用此方法,则会将所有的匹配都替换掉
System.out.println(matcher.appendTail(sb));
//使用lookingAt()要注意匹配会重新重头开始,以本次的String举例,调用会导致死循环,因为代码的最后又重头匹配了,而且nigoule这个字符串会由于被lookingAt匹配到从而导致输出中不会有它.
System.out.println(matcher.lookingAt());//若不想死循环,请注释掉这条代码.
//matcher()会导致lookingAt()一样的问题
System.out.println(matcher.matches());//若不想死循环,请注释掉这条代码.
System.out.println("--------------------------------------------");
//测试reset()方法,重置匹配字符串,CharSequences是字符串序列的抽象接口
//matcher.reset("nihao,世界helloworld");
}
}
四.Scanner工具类
Scanner类是util包中的一个工具类,主要用途就是扫描文本以获得数据,它大大减少了扫描输入的工作量.
在API文档中,可以看到,Scanner中有获得各种各样基本数据类型的方法,同时,他也可以获得匹配正则表达式的文本.