13.1 不可变String
String对象是不可变的。String类中每个看起来会修改String值的方法,实际都是创建了一个新的String对象。
13.2 重载+和StringBuilder
重载操作符的意思是一个操作符被用于特定的类时,被赋予了特殊的意义。用于String的“+”和“+=”是
Java中仅有的两个重载过的操作符,而且java不需要程序员重载任何操作符。问题在于+,会产生新的String对象用于包含原来的两个String对象。(除非你用代码将系统实现,并让它动起来,否则你不会了解有什么问题。)
虽然编译器会为我们优化+为StringBuilder,但在每一次循环中都会新建StringBuiler,所以还是使用StringBuiler好。 包括insert,replace,subString,reverse,append,toString,delete
String result= "";
for(int i=0; i<fields.length;i++)
result+=fields[i];
13.3 无意识的递归
标准容器覆盖了String方法,用于表达容器自身,以及容器包含的对象。
// 使用this不能完成打印变量内存地址的功能,
import java.util.*;
public class InfiniteRecursion {
public String toString(){
return " InfiniteRecursion address:"+this+"\n"
// 编译器看到String对象跟一个+,而后面不是String对象,会调用this的toString方法,调用自身。改用super.tostring()方法。
}
public static void main(String[] args) {
List<InfiniteRecursion> v = new ArrayList<>();
for(int i=0; i<10; i++)
v.add(new InfiniteRecursion());
System.out.println(v);
}
}
13.4 String上的操作
方法 | 参数 | 应用 |
---|---|---|
length | ||
charAt | ||
getChars | ||
getBytes | ||
equalsIgnoreCase | ||
compareTo | String | 按字典顺序比较String内容,比较结果为负数,0,正数。大小写并不等价。 |
contains | 要搜索的CharSequence | |
contentEquals | CharSquence和StringBuffer | |
startsWith endsWith | ||
indexOf lastIndexOf | char String | |
subString | ||
toLowerCase toUpperCase | ||
trim | 将String两端的空白字符删除 |
13.5 格式化的输出
13.5.1 printf
printf("Row 1: [%d %f]\n",x,y);
13.5.2 System.out.format()
与printf等价
public class SimpleFormat {
public static void main(String[] args){
int x = 5;
double y = 5.332352
//The old way,pretty complicated to deal with
System.out.println("Row 1: ["+x+" "+y+"]");
// The new way
System.out.format("Row 1: [%d %f]\n",x,y);
// or
System.out.printf("Row 1: [%d %f]\n",x,y);
}
}
13.5.3 Formatter类
当你新建一个Formatter对象的时候,需要向其构造器传递信息,指出最终结果将向哪里输出。
package Ch12;
import java.io.*;
import java.util.*;
public class Turtle {
private String name;
private Formatter f;
public Turtle(String name, Formatter f){
this.name = name;
this.f = f;
}
public void move(int x, int y){
f.format("%s The Turtle is at (%d,%d)\n", name, x, y);
}
public static void main(String[] args) {
PrintStream outAlias = System.out;
Turtle tommy = new Turtle("Tommy",new Formatter(System.out));
Turtle terry = new Turtle("Terry", new Formatter(outAlias));
tommy.move(0,0);
terry.move(4,8);
tommy.move(3,4);
terry.move(2,5);
tommy.move(3,3);
terry.move(3,3);
}
}
output:
Tommy The Turtle is at (0,0)
Terry The Turtle is at (4,8)
Tommy The Turtle is at (3,4)
Terry The Turtle is at (2,5)
Tommy The Turtle is at (3,3)
Terry The Turtle is at (3,3)
13.5.4 格式化说明符
%[argument_index$][flags][width][.precision]conversion
width指明最小尺寸,Formatter必要时通过添加0,来达到最小尺寸。
默认右对齐,不过可以使用“-”来改变对齐方向。
presion–>与width对应指明最大尺寸。
应用字符时,表示打印的最大字符长度。
应用于浮点,表示小数后面显示出来的部分,默认六位。如果小数过度则舍入,太少则补零。 由于整数没有小数,应用presion,会触发异常。
package Ch12;
import java.util.*;
public class Receipt {
private double total = 0;
private Formatter f = new Formatter(System.out);
public void printTitle(){
f.format("%-15s %5s %10s\n", "Item", "Qty", "Price");
// %-15s,-左对齐,15是width,因为是String后面补空格。 两个元素之间还有一个默认空格。所以一共是16个单位。
f.format("%-15s %5s %10s\n", "----", "---", "-----");
}
public void print(String name, int qty, double price){
f.format("%-15.15s %5d %10.2f\n", name, qty, price);
// %-15.15s,与%15s一致,小数点后面15代表最大长度。
total+=price;
}
public void printTotal() {
f.format("%-15s %5s %10.2f\n", "Tax", "", total * 0.06);
f.format("%-15s %5s %10s\n", "","","-----");
f.format("%-15s %5s %10.2f\n", "Total", "", total * 1.06);
}
public static void main(String[] args) {
Receipt receipt = new Receipt();
receipt.printTitle();
receipt.print("Jack's Magic Beans",4, 4.25);
receipt.print("Princess peas", 3, 5.1);
receipt.print("Three Bears Porridge",1,14.29);
receipt.printTotal();
}
}
output:
Item Qty Price
---- --- -----
Jack's Magic Be 4 4.25
Princess peas 3 5.10
Three Bears Por 1 14.29
Tax 1.42
-----
Total 25.06
13.5.5 Formatter转换
header 1 | 代表 |
---|---|
d | 整数型(十进制) |
e | 浮点数(科学计数法) |
c | Unicode字符 |
x | 整数(十六进制) |
b | Boolean值 |
h | 散列码 |
s | String |
% | 字符% |
f | 浮点数(十进制) |
将不为null的参数转化为boolean为真,即使0也是真。
13.5.6 String.format()
String.format()内部也是创建一个Formatter对象,然后将你传入的参数转给Formatter对象。
当你需要使用format方法一次时,这个更清晰。
13.6 正则表达式–处理字符串问题
13.6.1 基础
正则表达式就是通过某种方式来描述字符串
\d表示一位数字
在其他语言中,\\表示“在正则表达式中插入普通的反斜线,没有特殊意义。”
在java中则是我要插入一个正则表达式的反斜线,所以其后字符有特殊意义。\\\\
表达普通字符\。 前两个\,表示插入正则式子,后一个\是正则的,最后一个是要插入字符字符。 \\/是/
public class IntegerMatch {
public static void main(String[] args) {
System.out.println("-1234".matches("-?\\d+"));
System.out.println("5678".matches("-?\\d+"));
System.out.println("+911".matches("-?\\d+"));
System.out.println("+911".matches("(-|\\+)?\\d+"));
// | 表示或,()将表达式分组
}
}
output:
true
true
false
true
String自带的split()和replace方法。
replaceFirst(“regex”,String s);
replaceAll(“regex”,String s);
13.6.2
字符 | 意义 |
---|---|
B | 指定字符B |
\xhh | 十六进制为0xhh的字符 |
\uhhhh | 十六进制为 0xhhhh的unicode字符 |
\t | 制表符tab |
\n | 换行符 |
\r | 回车 |
\f | 换页 |
\e | 转义(Escape) |
字符类 | 意义 |
---|---|
. | 任意字符 |
[abc] | a|b|c |
[^abc] | 除了abc之外的所有字符 |
[a-zA-Z] | 从a到z,A到Z的所有字符 |
[adc[hij]] | a |
[a-z&&[hij]] | 任意h,i或j的交。 |
\s | 空白字符(空白,tab,换行,换页,回车) |
\S | 非空白字符([^\s]) |
\d | 数字[0-9] |
\D | 非数字 |
\w | 词字符[a-zA-Z0-9] |
\W | 非词字符[^\w] |
边界匹配符 | meaning |
---|---|
^ | 一行的起始 |
\B | 非词的边界 |
$ | 一行的结束 |
\G | 前一个匹配的结束 |
\b | 词的边界 |
13.6.3 量词
描述一个模式吸收输入文本的模式
贪婪型 X? 为所有可能的模式发现尽可能多的匹配
勉强型 X?? 匹配满足模式最少的字符数
占有型 X?+ 只在java中,防止回溯,
header1 | meaning |
---|---|
X? | 一个或者零个X |
X* | 或者多个零个X |
X+ | 一个或者多个X |
X{n} | 恰好n次X |
X{n,} | 至少n次x |
X{n,m] | 至少n次,至多m次 |
row 2 col 1 | row 2 col 2 |
13.6.4 Pattern和Matcher
Pattern p = Pattern.compile(regex);
static boolean matches(String regex, CharSequence input)
split() replace()
Matcher m = p.matcher(testString);
m.matches() testString 是否匹配regex
m.lookingAt() testString 某部分开始是否匹配
m.find()
m.find(int start)
find()在charSequence查找多个匹配字符
package Ch12;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Finding {
public static void main(String[] args) {
Matcher m = Pattern.compile("\\w+").matcher("Evening is full of linnet's wings");
while(m.find()){
System.out.print(m.group()+" ");
}
System.out.println();
int i=0;
while(m.find(i)){
System.out.print(m.group()+" ");
i++;
}
}
}
Evening is full of linnet s wings
Evening vening ening ning ing ng g is is s full full ull ll l of of f linnet linnet innet nnet net et t s s wings wings ings ngs gs s
Groups
A(B(C)D) 组0是整个正则表达式 ABCD,组1是BC,组2是C
public int groupCount() 返回匹配器的模式中分组数目,不包括0组
public String group() 返回前一次匹配操作的第0组,
public int start()返回前一次匹配中找到的组的起始索引,
public int end()返回前一次匹配中找到的组的最后一个字符索引+1的值,
pattern 标记
13.6.5 split()
String[] split(CharSequence?? String input)
String[] split(CharSequence?? input, int limit)
package Ch12;
import static tools.Print.print;
import java.util.*;
import java.util.regex.*;
public class SplitDemo {
public static void main(String[] args) {
String input = "This!!unusual use!!of exclamation!!points";
print(Arrays.toString(Pattern.compile("!!").split(input)));
print(Arrays.toString(Pattern.compile("!!").split(input,3)));
}
}
[This, unusual use, of exclamation, points]
[This, unusual use, of exclamation!!points]
13.6.6 替换操作
package ch13;
//: Strings/TheReplacement.java
import java.util.regex.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
public class TheReplacement {
public static void main(String[] args) throws Exception{
String s = "/*! Here's a block text to use as input to " +
"the regular expression matcher. Note that we'll " +
"first extract the block of textg by looking for the special delimiters, then process the " +
"extracted block. !*/";
//原来是一个文件读取有问题,我直接改成该字符串
Matcher mInput = Pattern.compile("/\\*!(.*)!\\*/",Pattern.DOTALL).matcher(s);
if(mInput.find())
s = mInput.group(1);
//replaceFirst和replaceAll是String自带方法。
//appendReplacement是Matcher类的方法。
s = s.replaceAll(" {2,}"," ");//将2个或者多个空格替换为1个
s = s.replaceAll("(?m)^ +","");//每行开头的多个空格去掉。
print(s);
s = s.replaceFirst("[aeiou]","(VOWEL1)");
StringBuffer subf = new StringBuffer();
Pattern p = Pattern.compile("[aeiou]");
Matcher m = p.matcher(s);
while(m.find())
m.appendReplacement(subf, m.group().toUpperCase());
m.appendTail(subf);// 所谓剩余未处理实际是处理后的字符串。
print(subf);
}
}
output:
Here's a block text to use as input to the regular expression matcher. Note that we'll first extract the block of textg by looking for the special delimiters, then process the extracted block.
H(VOWEL1)rE's A blOck tExt tO UsE As InpUt tO thE rEgUlAr ExprEssIOn mAtchEr. NOtE thAt wE'll fIrst ExtrAct thE blOck Of tExtg by lOOkIng fOr thE spEcIAl dElImItErs, thEn prOcEss thE ExtrActEd blOck.
13.6.7 reset()
m.reset()将现有Matcher对象用于新的字符序列
package ch13;
import java.util.regex.*;
public class Resetting {
public static void main(String[] args) throws Exception{
Matcher m = Pattern.compile("[frb][aiu][gx]").matcher("fix the rug with bags");
while(m.find())
System.out.print(m.group()+" ");
System.out.println();
m.reset("fix the rag with rags");
while(m.find()){
System.out.print(m.group()+" " +
"");
}
}
}
output:
fix rug bag
fix rag rag
13.6.8 正则表达式和java I/O
??
13.7 扫描输入
package ch13;
import java.io.*;
public class SimpleRead {
public static BufferedReader input = new BufferedReader(new StringReader("Sir Robin of Camelot\n22 1.61803"));
// StringReader 将String转化为可读的流对象。
public static void main(String[] args) {
try{
System.out.println("What is your name");
String name = input.readLine();
System.out.println(name);
System.out.println("How old are you? What is your favorite double");
System.out.println("(input: <age> <double>)");
String numbers = input.readLine();
System.out.println(numbers);
String[] numArray = numbers.split(" ");
int age = Integer.parseInt(numArray[0]);
double favorite = Double.parseDouble(numArray[1]);
System.out.format("Hi %s \n", name);
System.out.printf("In 5 years you will be %d.\n", age+5);
System.out.printf("My favorite is %f", favorite/2);
}catch (IOException e){
System.err.println("I/O exception");
}
}
}
//Scanner 类
package ch13;
import com.sun.org.apache.xpath.internal.SourceTree;
import java.util.*;
public class BetterRead {
public static void main(String[] args) {
Scanner stdin = new Scanner(SimpleRead.input);
System.out.println("what is your name");
String name = stdin.nextLine();
System.out.println(name);
System.out.println("How old are you?What is your favorite double");
System.out.println("(input: <age> <double>)");
int age = stdin.nextInt();
double favorite = stdin.nextDouble();
System.out.println(age);
System.out.println(favorite);
System.out.printf("Hi %s \n", name);
System.out.printf("In five years you will be %d \n", age +5);
System.out.printf("My favorite double is %f,",favorite/2);
}
}
13.7.1 Sacnner定界符
package ch13;
import java.util.*;
public class ScannerDelimitter {
public static void main(String[] args) {
Scanner scanner = new Scanner("12, 42, 78, 99, 42");
scanner.useDelimiter("\\s*,\\s");
while(scanner.hasNextInt()){
System.out.println(scanner.nextInt()
);
}
}
}
12
42
78
99
42
13.7.2 用正则表达式扫描
??