13章_字符串

13.1

String对象是不可变的。涉及String值的操作都是创建了一个全新的String对象,原来的String值一点也没有变。

13.2

(1)用于String的+与+=是Java中仅有的两个重载过的操作符,并不允许程序员重载任何操作符。

(2)“+“号的内部实现:

String s = "abc" + mango + "def" + 47;

编译时编译器自动引入了java.lang.StringBuilder类,用它的append()方法完成字符串拼接,最后用它的toString()方法返回结果字符串。

(3)在循环体中使用"+"和使用自定义StringBuilder对象的区别:

我们知道使用"+"连接字符串编译器会自动优化,自动创建StringBuilder进行连接,那么每循环一次都会创建一个新StringBuilder对象进行连接操作,比较消耗资源。

String[] str = {"A","B","C"};

String re ;

for(int i=0;i

  re +=str[i];

}

而显式使用StringBuilder连接字符串,循环体只是用我们定义的一个StringBuilder进行字符串连接,效率更高。

StringBuilder sb = new StringBuilder();

for(int i=0;i

sb.append(str[i]);

}

StringBuilder是Java SE5引入的,在此之前使用StringBuffer,StringBuffer是线程安全的,因此开销也会大一些。

(4)注意append方法里面使用"+"连接字符串作为参数,效率反而比单使用"+"低。原因看(2)。

例如:

StringBuilder sb = new StringBuilder();//第一个StringBuilder

sb.append( + valve1 + );//第二个StringBuilder

sb.append( + valve2 + );//第三个StringBuilderreturn sb.toString();

这种情况下使用javap -c查看,会创建3个StringBuilder,一个是我们创建的,另外两个是“+”操作编译器优化创建的。这种情况表面上使用了StringBuilder,而且代码看起来也很整洁,但是效率却没有以下形式高:

+ valve1 +  +

+ valve2 +  +

因为编译器自动替我们优化,仅仅使用了一个StringBuilder。

13.4

String类的大多数方法,当需要改变字符串的内容时,方法都返回一个新的String对象;如果内容没有改变方法仅仅返回指向原字符串对象的引用。

13.5

(1)从Java 1.5开始加入了c中printf()函数的功能,它可以用于 PrintStream类和PrintWriter类,因为System.out对象是一个PrintStream对象,故它拥有format()方法和printf()方法(两者功能一样,等同于c中的printf()方法用于格式化输出)。

System.out.printf("Row 1:[%d,%f]\n",5,3.1455);

System.out.format("Row 1:[%d,%f]\n",5,3.1455);

(2)Java中新的格式化输出功能都由java.util.Formatter类处理。

Formatter fo = new Formatter(System.out);

fo.format("Row 1:[%d,%f]\n",5,3.1455);

Formatter构造函数经过重载可以接受多种输出目的地,不过最常用的还是PrintStream、OutputStream和FIle。

(3)格式化说明符(控制输出的空格和对齐)

语法如下:

%[argument_index$][flags][width][.precision]conversion

argument_index是一个十进制整数,用于表面参数在参数列表中的位置。第一个参数由“1$”引用,第二个参数由“2$”引用,以此类推;

flags是类型转换字符;

d 十进制整数 c Unicode字符 b Boolean值 s String值 f 十进制浮点数

e 科学计数法浮点数 x 十六进制整数 h 十六进制散列码 % 字符"%"

width指定输出的最小宽度,默认是右对齐,可通过添加-改变对齐方向。

.precision,应用于String表示打印出字符的最大长度,应用于浮点数表示小数点后要显示出的位数,超出的做舍入运算,不够的添加0,默认是6位。不可应用于整数。

conversion是表明应该如果格式化参数的字符。

例子:

Formatter fo = new Formatter(System.out);

fo.format("%-15s %5s %10s\n","Item","Qty","Price");

fo.format("%-15s %5s %10s\n","----","---","-----");

输出效果:

Item      Qty    Price

----      ---    -----

注意:对于boolean类型或Boolean对象,%b的转换结果是true或false,对其它类型的参数,只要不是Null都会输出true,经过编码测试

int i = 0;

f.format(, i);

输出的依旧是true。

(4)Java 1.5在String类中添加了静态方法format(),它接受与Formatter.format一样的参数,但返回一个String对象。其实在String.format()内部也使用Formatter完成相应的任务。

13.6

1> String可以使用正则表达式的方法:

matches() ----可以判断该String对象是否匹配方法参数表示的正则表达式。

replaceFirst()或者replaceAll() ----能将对象中成功匹配正则表达式的部分替代为参数中的字符串。

split() ----将字符串从正则表达式匹配的地方切开。

2> 正则表达式量词的三种类型:

(1)Greediness(贪婪型):最大匹配

        X?、X*、X+、X{n}、X{n,}、X{n,m}都是最大匹配。例如你要用 “<.+>”去匹配“aaavaabb”,也许你所期待的结果是想匹配 “”,但是实际结果却会匹配到“aava”。这是为什么呢?下面我们跟踪下最大匹配的匹 配过程。

①“<”匹配字符串的“<”。②“.+”匹配字符串的“tr>aavaab”,在进行最大匹配时,它把两个 “>”都匹配了,它匹配了所有字符,直到文本的最后字符“b”③这时,发现不能成功匹配“>”,开始按原路回退,用“a”与“>”匹 配,直到“ab”前面的“>”匹配成功。

(2)Reluctant(Laziness)(勉强型):最小匹配

        X?、X*、X+、X{n}、X{n,}、X{n,m}都是最大匹配。好,加个?就成了Laziness匹配。例如X??、X*?、X+?、X{n}?、X{n,}?、X{n,m}?都是最小匹配,其实X{n,m}?和X{n }?有些多余。

最小匹配意味者,.+? 匹配一个字符后,马上试一试>的匹配可能,失败了,则.+?再匹配一个字符,再马上试一试>的匹配可能。JDK文档中Greedy 和Reluctant,它是以eat一口来隐喻的,所以翻译成贪吃和(勉强的)厌食最贴切了。不过我喜欢最大匹配、最小匹配的说法。

(3)Possessive(占有型):完全匹配

与最大匹配不同,还有一种匹配形式:X?+、X*+、X++、X{n}+、X{n,}+、X{n,m}+等,成为完全匹配。它和最大匹配一样,一直匹配所有的字符,直到文本的最后,但它不原路返回。

这个不大好理解,有一个例子摘自http://bbs.csdn.net/topics/390269371比较形象:

字符串为aaa,正则表达式为[a]*+,这是一个贪婪的匹配,直接返回aaa。但是如果是占有型的,正则为[a]*+a,返回结果是false,是空。

如果a*a,*是匹配优先的,也就是说先匹配,如果正则的后续部分不能再匹配,就回溯,在这个例子中,匹配字符串aaa的时候,首先a*匹配到最后一个,然后发现正则后面还有一个a没法匹配,就会将a*回溯到字符串的中间一个a,这时候正则中的最后一个a与字符串的最后一个a正好匹配,匹配结束。

如果正则是a*+a,*+是占有优先,也就是说*+前面的字符会尽可能匹配,匹配了的就不会再回溯,不会让回去了,即所谓占有。如果字符串是aaa,那么这个例子中匹配过程就是a*+匹配了字符串的三个a,正则中的最后一个a不会再被匹配,因为a*+不会回溯。

3> 接口java.lang.CharSequence从CharBuffer、String、StringBuilder和StringBuffer类中抽象出了字符序列的一般定义:

interface CharSequence {

    charAt(int i);

    length();

    subSequence(int start, int end);

    toString();

}

多数正则表达式的操作都接受CharSequence类型的参数。

4> Pattern 对象( java.util.regex.Pattern)

静态方法:

static boolean Pattern.matches(String regex,CharSequence input )

该方法用以检查regex是否匹配了整个input参数。编译后的Pattern对象还提供了split()方法,它从匹配了regex的地方分隔输入字符串,返回分隔后的子字符串String数组。

注意:静态方法Pattern.matches 和 对象方法p.matcher()的拼写。

其它更强的操作要使用Matcher对象:

String regex = "a*+";//正则表达式

Pattern p = Pattern.complie(regex) ; //编译正则表达式生成比String正则功能更强大的Pattern对象。

Matcher m = p.matcher();//matcher()方法会生成一个Matcher对象。

Matcher 常用方法:

boolean matches() ----用来判断整个输入字符串是否匹配正则表达式模式

boolean  lookingAt() ---用来判断该字符串(不必是整个字符串)的始部分是否能够匹配模式

boolean  find() ---- 尝试查找与该模式匹配的输入序列的下一个子序列。

boolean  find(int i) ---find()重载版本,以i位置作为搜索的起点。

注意:

(1)find()可以在输入的任意位置定位正则表达式,而lookingAt()和matches()只有在正则表达式与输入的最开始处就开始匹配时才会成功。matches()只有在整个输入都匹配正则表达式时才会成功,而lookingAt()只要输入的第一部分匹配就会成功。

(2)如果使用

while(m.find(i)) {}

这种形式,一定记得在循环里改变i的值或者加入break条件,否则每次m.find(i)都从固定的位置开始查找匹配,会陷入死循环。

5> (?i)在Java的正则表达式中表示忽略大小写。

6> 组是用括号划分的正则表达式,可以用组号来引用组,组号为0表示整个正则表达式,组号为1表示第一个括号包含的正则表达式,以此类推。例如:

A(B(C))D

group0表示ABCD,group1表示BC,group2表示C。

Matcher类的对象有一系列获取组相关信息的方法:

public int groupCount()返回该匹配器模式中的分组数目,第0组不包括在内。

public String group()返回前一次匹配模式操作(例如find())的第0组。

public String group(int i )返回前一次匹配模式操作指定组号的组,如果匹配成功,但是指定的组没有匹配输入字符串的任何部分,则会返回Null;

public in start(int group)返回在前一次匹配操作中寻找到的组的起始索引;

public int end(int group)返回在前一次匹配操作中寻找到的组的最后一个字符索引加一的值。

7>匹配操作失败之后(或先与一个正在进行的匹配操作去尝试)调用start()或end()将会产生IllegalStateException。

8> Pattern类的compile()方法有一个重载版本,它接受一个flag参数,以调整匹配的行为:

Pattern Pattern.compile(String regex, int flag)

flag类型:

Pattern.CANON_EQ,当且仅当两个字符的"正规分解(canonical decomposition)"都完全相同的情况下,才认定匹配。比如用了这个标志之后,表达式"a\u030A"会匹配"?"。默认情况下,不考虑"规范相等性(canonical equivalence)"。

Pattern.CASE_INSENSITIVE(?i) 默认情况下,大小写不敏感的匹配只适用于US-ASCII字符集。这个标志能让表达式忽略大小写进行匹配。要想对Unicode字符进行大小不明感的匹 配,只要将UNICODE_CASE与这个标志合起来就行了。

Pattern.COMMENTS(?x) 在这种模式下,匹配时会忽略(正则表达式里的)空格字符(不是指表达式里的"\s",而是指表达式里的空格,tab,回车之类)。注释从#开始,一直到这行结束。可以通过嵌入式的标志来启用Unix行模式。

Pattern.DOTALL(?s) 在这种模式下,表达式'.'可以匹配任意字符,包括表示一行的结束符。默认情况下,表达式'.'不匹配行的结束符。

Pattern.MULTILINE(?m)在这种模式下,'^'和'$'分别匹配一行的开始和结束。此外,'^'仍然匹配字符串的开始,'$'也匹配字符串的结束。默认情况下,这两个表达式仅仅匹配字符串的开始和结束。

Pattern.UNICODE_CASE(?u) 在这个模式下,如果你还启用了CASE_INSENSITIVE标志,那么它会对Unicode字符进行大小写不明感的匹配。默认情况下,大小写不敏感的匹配只适用于US-ASCII字符集。

Pattern.UNIX_LINES(?d) 在这个模式下,只有'\n'才被认作一行的中止,并且与'.','^',以及'$'进行匹配。

来源: http://blog.sina.com.cn/s/blog_4b93170a0100ornk.html

Pattern.Case_INSENSITIVE、Pattern.NULTILINE以及Pattern.COMMENTS比较常用。另外,可以直接在正则表达式中使用其中大多数标记,只要将上表括号括起的字符插入到正则表达式中希望起到作用的位置即可。

8> Pattern对象的

String[] split(CharSequence input)

String[] split(CharSequence input, int limit)

将输入字符串根据正则表达式断开成字符串对象数组。第二种形式的split()方法可以限制分割成字符串的数量。

9> Matcher类的对象有一个强大的appendReplacement(StringBuffer, String)方法,它执行以下操作:

(1)从添加位置开始在输入序列读取字符并将其添加到StringBuffer中,在匹配之前的那一字符停止;

(2)将给定的字符串添加到StringBuffer;

(3)将此匹配器的添加位置设置为最后匹配位置的索引加1,即end();

例子:

Matcher m = Pattern.compile().matcher(); 

StringBuffer sb = new StringBuffer(); 

while(m.find()){ 

    m.appendReplacement(sb, ); 

m.appendTail(sb); 

System.out.println(sb); 

输出的结果是11a,11a,11,a;22b,22b,22,b;

正则表达式((\d\d)(\w))有三个分组,每个括号包含的内容称作一个分组,并按照左括号出现的顺序给每个分组给予编号1,2,3,...,编号为0的分组代表整个被匹配的字符串。

例子程序中,

$0是整个字符串,“11a”被 ((\d\d)(\w))匹配,等于11a,此时sb是:11a

$1是(\d\d)(\w)匹配的内容,等于11a,此时sb是11a,11a

$2是(\d\d)匹配的内容,等于11,此时sb是11a,11a,11

$3是(\w)匹配的内容,等于a,此时sb是11a,11a,11,a;

来源: http://lazy2009.iteye.com/blog/1671157

替换字符串还可以包含匹配的组引用,$g将被group(g)的计算结果替换。在执行一次或多次appendReplacement()之后,调用appendTail(StringBuffer)方法将输入字符串剩余的部分复制到sbuf中。

10> Matcher对象的reset()方法

带参reset()方法:将当前Matcher对象应用于新的字符序列。

不带参reset()方法:重新设置到当前字符序列的起始位置。

13.7

(1)Java 1.5新增了java.util.Scanner类,它的构造器可以接受任何类型的输入对象,包括File对象、InputStream、String或者Readable对象。

(2)Readable是1.5新增的接口,表示具有read()方法的某种类。

(3)有了Scanner,所有的输入、分词以及翻译的操作都隐藏在不同类型的next方法中。普通的next()方法返回下一个String。所有基本类型(除char之外)都有对应的next方法,包括BigDecimal和BigInteger。所有的next方法只有在找到一个完整的分词之后才会返回。还有相应的hasNext方法,用来判断下一个输入分词是否是所需的类型。

区别旧方式需要截取字符串并使用不同的数据类型把字符串转成对应的数据相比Scanner只需使用nextXXX()方法就可以提取字符串的数据并转换成想要的数据类型。

例如:

String str= "20 1.34";

旧方式

String[] strs = str.split(" ");

int age = Integer.parseInt(strs[0]);

double fa = Double.parseDouble(strs[1]);

Scanner方式

Scanner sc = new Scanner(str);

int age = sc.nextInt();

double fa = sc.nextDouble();

(4)Scanner的操作不会抛出IOException,而是将它们吞掉,可以使用ioException()方法返回最近底层产生的IOException。

(5)默认情况下Scanner使用空白字符对输入进行分词,可以使用正则表达式指定自己需要的定界符:

useDelimiter(String regex);

delimiter()方法用来返回当前正在作为定界符使用的Pattern对象。

(6)使用正则表达式扫描

hasNext(Pattern) ---通过正则Pattern对象判断是否有下一个匹配该模式的输入部分。

next(Pattern) ---找到下一个匹配该模式的输入部分。

match() ---获得匹配结果,返回的是一个MatchResult对象。

需要注意的是,它仅仅针对下一个分词进行匹配,如果正则表达式中含有定界符,匹配永远不会成功。

13.8

在Java引入正则表达式(1.4开始)和Scanner类后(1.5开始)之前,分割字符串的唯一方式是使用StringTokenizer,有了正则表达式和Scanner搭配使用后分割字符串变得更加简单。

你可能感兴趣的:(13章_字符串)