第13章 字符串

第13章 字符串

13.1 不可变String

public class Test {
    public static void main(String[] args) {
        String a = "abc";
        String b = f(a);
        System.out.println(a);
        System.out.println(b);
    }

    public static String f(String s){
        return s.toUpperCase();
    }
}
/*
abc
ABC
*/

String类型是不可变的,向方法中传递的参数,是指向String对象的一个引用;而对方法而言,参数是传递信息用的,而不是让该方法改变自己的。

13.2 重载“+”与StringBuilder

String对象是不可变的,指向它的任何引用都不能改变它的值,但它也会造成一些效率问题。

# 生成JVM字节码(可以方便的查看代码的底层原理)
javap -c class文件

+ 底层是创建StringBuilder对象,使用append()方法,连接字符串,其存在的问题是当处于循环状态时,每次循环都会创建一个StringBuilder对象,因此,速度会变慢。

public class Test {
    public static void main(String[] args) {
        String s = "a";
        for (int i = 0; i < 10; i++) {
            s += "b";
        }
    }
}
Compiled from "Test.java"
public class com.ding.five.Test {
  public com.ding.five.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":
()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String a
       2: astore_1
       3: iconst_0
       4: istore_2
       5: iload_2
       6: bipush        10
       8: if_icmpge     37
      11: new           #3                  // class java/lang/StringBuilder
      14: dup
      15: invokespecial #4                  // Method java/lang/StringBuilder."<
init>":()V
      18: aload_1
      19: invokevirtual #5                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: ldc           #6                  // String b
      24: invokevirtual #5                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      27: invokevirtual #7                  // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
      30: astore_1
      31: iinc          2, 1
      34: goto          5
      37: return

分析: 从第8行到34行构成了循环体,每次都会新建一个StringBuilder对象。

public class Test {
    public static void main(String[] args) {
       StringBuilder s = new StringBuilder("a");
        for (int i = 0; i < 10; i++) {
            s.append("b");
        }
    }
}
Compiled from "Test.java"
public class com.ding.five.Test {
  public com.ding.five.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":
()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup
       4: ldc           #3                  // String a
       6: invokespecial #4                  // Method java/lang/StringBuilder."<
init>":(Ljava/lang/String;)V
       9: astore_1
      10: iconst_0
      11: istore_2
      12: iload_2
      13: bipush        10
      15: if_icmpge     31
      18: aload_1
      19: ldc           #5                  // String b
      21: invokevirtual #6                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: pop
      25: iinc          2, 1
      28: goto          12
      31: return

分析 在第0行创建了StringBuilder对象,而从15到28行的循环体并没有创建对象,因此,与上种方法对比,速度会更快。

+和StringBuilder对比

  public static void main(String[] args) {
        String s = "a";
        long l = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            s = s +"a";
        }
        System.out.println(System.currentTimeMillis() - l); // 120
        StringBuilder stringBuilder = new StringBuilder("a");
        l = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            stringBuilder.append("a");
        }
        System.out.println(System.currentTimeMillis() - l); // 1
    }
// 可以看到后者的速度远远快于前者,但是如果没有达到一个数量级,几乎没有影响

注意:stringBuilder.append("key"+":"+"value");千万不要这样写,编译器会为括号内的操作创建StringBuilder对象。

13.3 无意识的递归

使用this打印内存地址

public class Pet {

    @Override
    public String toString() {
        return "内存地址:"+this;
    }
}

 public static void main(String[] args) {
       Pet p = new Pet();
        System.out.println(p);
    }
/*
Exception in thread "main" java.lang.StackOverflowError
	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449)
	at java.lang.StringBuilder.append(StringBuilder.java:136)
	at com.ding.five.Pet.toString(Pet.java:12)
*/

原因: 当打印对象时,会调用该对象的toString()方法,当一个对象与String类型相加时,会调用该对象的toString方法,将该对象转为String类型,因此会导致递归调用。

正确处理

public class Pet {

    @Override
    public String toString() {
        return "内存地址:"+super.toString();
    }
}
 public static void main(String[] args) {
       Pet p = new Pet();
        System.out.println(p);
    }
/*
内存地址:com.ding.five.Pet@1540e19d
*/

13.4 String上的操作

 public static void stringOption() throws UnsupportedEncodingException {
        byte[] bytes = {97,98,99};
        char[] chars = {'a','b','c'};
        // 创建String对象
        String string = "abc";
        string = new String(bytes);
        System.out.println(string);
        string = new String("abc");
        string = new String(chars);
        string = new String(new StringBuilder("abc"));
        string = new String(new StringBuilder("abc"));
        // 字符个数
        int length = string.length(); // 3
        // 指定索引位置的字符
        char c = string.charAt(1); // b
        // 获得字节数组
        bytes = string.getBytes();
        bytes = string.getBytes("UTF-8"); // 指定编码
        // 复制到指定字符数组
        string.getChars(0,2,chars,1); // [a, a, b]
        // 生成一个字符数组
        chars = string.toCharArray();
        // 比较内容是否相同
        boolean equals = string.equals("abc");
        // 忽略大小写比较
        boolean equalsIgnoreCase = string.equalsIgnoreCase("ABC"); // true
        // 按ASCII码比较
        int compareTo = string.compareTo("aeb"); // -3
        // 按ASCII码忽略大小写比较
        int compareToIgnoreCase = string.compareToIgnoreCase("ABC"); // 0
        // 是否包含str
        boolean contains = string.contains("bc"); // true
        //  比较内容是否相同
        boolean contentEquals = string.contentEquals("abc"); // true
        contentEquals = string.contentEquals(new StringBuffer("abc")); // true
        // 比较部分内容是否相同
        boolean regionMatches = string.regionMatches(1, "efhbcdd", 3, 2); // true
        // 忽略大小写
        string.regionMatches(true,1,"efhBCdd",3,2); // true
        // 起始字符串
        boolean startsWith = string.startsWith("ab"); // true
        // 结束字符串
        boolean endsWith = string.endsWith("bc"); // true
        // 字符的索引
        int indexOf = string.indexOf(97); // 0
        indexOf = string.indexOf("bc"); // 1
        indexOf = string.indexOf(97,1); // -1
        // 截取子字符串
        String substring = string.substring(1); // bc
        substring = string.substring(0,1); // a
        // 连接字符串
        String concat = string.concat("xyz");
        // 替换
        String replace = string.replace("b", "e"); // aec
        // 改变大小写
        String upperCase = string.toUpperCase(); // ABC
        String lowerCase = upperCase.toLowerCase(); // abc
        // 删除两端空白符
        String trim = " a ".trim();
        System.out.println("1"+trim+"1"); // 1a1
        // 转为字符串
        String valueOf = String.valueOf(1);
    }

intern()方法

   public static void main(String[] args)  {
        String s = new String("abc");
        String s1 = "abc";
       // 先去字符串常量池寻找,有返回引用,没有创建并返回引用
        String s2 = s.intern();
        System.out.println(s == s1); // false
        System.out.println(s == s2); // false
        System.out.println(s1 == s2); // true
    }

13.5 格式化输出

 public static void main(String[] args){
        int x = 12;
        double y = 133.2;
        System.out.printf("age:%d high:%f",x,y); // age:12 high:133.200000
        System.out.println();
        System.out.format("age:%d high:%f",x,y); // age:12 high:133.200000
    }

Formatter类

所有格式化功能都由Formatter处理。

 public static void main(String[] args){
     // 指定输出位置
        Formatter f = new Formatter(System.out);
        f.format("age:%d",12);
    }
// age:12

格式化符

public class Test {
    public static void main(String[] args){
        Formatter formatter = new Formatter(System.out);
     f(formatter,"name","high");
     f(formatter,"make",127.1);
     f(formatter,"makejens",122.459);
    }

    public static void f(Formatter f,String name,double high){
        // - 左对齐(默认右),10 指定最小尺寸 .2 浮点数保留位数/string的最大输出数
        f.format("%-10s%10.2f\n",name,high);
    }

    public static void f(Formatter f,String name,String high){
        f.format("%-10s%10s\n",name,high);
    }
}
/*
name            high
make          127.10
makejens      122.46
*/

转换符

符号说明
d整数(十进制)
cUnicode字符
bBoolean(非空为true,包括0)
sString
f浮点数(十进制)
e浮点数(科学计数)(1000 = 1*e3)
x整数(十六进制)
h散列码(十六进制)
%字符“%”

String中的格式化

String format = String.format("%d", 12);
System.out.println(format); // 12

二进制转十六进制工具

public class Test {
    public static void main(String[] args) throws IOException {
        FileInputStream in = new FileInputStream("e:/Test.java");
        byte[] bytes = new byte[1024];
        in.read(bytes);
        System.out.println(f(bytes));
        in.close();
    }

    public static String f(byte[] bytes){
        StringBuilder stringBuilder = new StringBuilder();
        int n = 0;
        for (byte b : bytes) {
            if (n % 16 == 0){
                // 位数用0补
                stringBuilder.append(String.format("%05x: ",n));
            }else {
                stringBuilder.append(String.format("%02x ",b));
            }
            n++;
            if (n % 16 == 0) {
                stringBuilder.append("\n");
            }
        }
        return stringBuilder.toString();
    }
}
/*
00000: 75 62 6c 69 63 20 63 6c 61 73 73 20 54 65 73 
00010: 20 7b 0d 0a 20 20 20 20 70 75 62 6c 69 63 20 
00020: 74 61 74 69 63 20 76 6f 69 64 20 6d 61 69 6e 
00030: 53 74 72 69 6e 67 5b 5d 20 61 72 67 73 29 20 
00040: 68 72 6f 77 73 20 49 4f 45 78 63 65 70 74 69 
00050: 6e 20 7b 0d 0a 20 20 20 20 20 20 20 20 46 69 
00060: 65 49 6e 70 75 74 53 74 72 65 61 6d 20 69 6e 
00070: 3d 20 6e 65 77 20 46 69 6c 65 49 6e 70 75 74 
00080: 74 72 65 61 6d 28 22 65 3a 2f 54 65 73 74 2e 
00090: 61 76 61 22 29 3b 0d 0a 20 20 20 20 20 20 20 
000a0: 62 79 74 65 5b 5d 20 62 79 74 65 73 20 3d 20 
000b0: 65 77 20 62 79 74 65 5b 31 30 32 34 5d 3b 0d 
000c0: 20 20 20 20 20 20 20 20 69 6e 2e 72 65 61 64 
000d0: 62 79 74 65 73 29 3b 0d 0a 20 20 20 20 20 20 
*/

13.6 正则表达式

正则表达式是一种强大而灵活的文本处理工具,我们可以以编程的方式,构造复杂的文本模式;其提供了一种通用的方式对字符串进行:匹配/选择/编辑/验证。

CharBuffer/String/StringBuffer/StringBuilder都实现了CharSequence接口,都可以进行正则表达式操作。

基础

	public static void main(String[] args)  {
		// 匹配格式
		System.out.println("123".matches("\\d+"));
		
		// 分割字符串
		String string = "a1b2c3e4";
		String[] strings = string.split("2");
		System.out.println(Arrays.toString(strings));
		strings = string.split("\\d");
		System.out.println(Arrays.toString(strings));
		// 2:分割出子字符串最多数
		strings = string.split("\\d", 2);
		System.out.println(Arrays.toString(strings));
		
		// 替换字符
		String rf = string.replaceFirst("\\d", "*");
		System.out.println(rf);
		String ra = string.replaceAll("\\d", "-");
		System.out.println(ra);
	}
/*
true
[a1b, c3e4]
[a, b, c, e]
[a, b2c3e4]
a*b2c3e4
a-b-c-e-
*/
13.6.2 创建正则表达式

字符

表达式含义
\t制表符Tab
\n换行符
\r回车
\f换页
\e转义

字符类

表达式含义
.任意字符
[abc]a或b或c
[^abc]非abc字符
[a-zA-Z]a-z或A-Z字符
[abc[hij]]abchij任意字符
\s空白符(空格/tab/换行/换页/回车)
\S非空白符
\d数字[0-9]
\D非数字[0-9]或[\d]
\w词字符[a-zA-Z0-9]
\W非词字符[^\w]

逻辑操作符

表达式含义
XYY跟在X后面
X|YX或Y
(X)捕获组,可以在表达式中用\i引用第i个捕获组

边界匹配符

表达式含义
^一行的开始
$一行的结束
\b词的边界
\B非词的边界
\G前一个匹配的结束
13.6.3 量词

贪婪型: 为表达式发现尽可能多的匹配。

勉强型: 用"?"指定,匹配表达式最少字符数。

占有型: java中才可用,用“+”指定,不保存中间状态,使执行更高效。

贪婪型勉强型占有型规则
X?X??X?+0或1个
X*X*?X*+0或多个
X+X+?X++1或多个
XX?X+恰好n个
X{n,}X{n,}?X{n,}+至少n个
X{n,m}X{n,m}?X{n,m}+n-m个
public static void main(String[] args)  {
		System.out.println(Arrays.toString("1aa23".split("a+")));
		System.out.println(Arrays.toString("1aa23".split("a+?")));
    /*
    [1, 23]
    [1, , 23]
    */
    // 可以看到,贪婪型的一次匹配了两个a,而勉强型一次匹配一个a
	}
13.6.4 Pattern和Matcher

基本

public static void main(String[] args)  {
		// 是否符合正则规则
		boolean b = Pattern.matches("\\d", "1");
		System.out.println(b); // true
		
		Pattern pattern = Pattern.compile("\\d"); // 匹配规则
		// 分离字符串
		String[] strings = pattern.split("a1b2c");
		System.out.println(Arrays.toString(strings)); // [a, b, c]
		
		Matcher matcher = pattern.matcher("a1b2");
		// 替换符合正则规则的元素
		String string = matcher.replaceAll("*");
		System.out.println(string); // a*b*
		
		// 判断字符串开始的部分是否符合正则规则
		boolean lookingAt = matcher.lookingAt();
		System.out.println(lookingAt); // false
		
		matcher = pattern.matcher("1a2bc5");
		// 类似于迭代器,向前遍历输入字符串
		while(matcher.find()){ // 注意:字符串始必须符合规则,否则无法继续
			System.out.println(matcher.group());
			/*
			 * 1
			 * 2
			 * 5
			 */
		}
		int i = 0;
		// 接收一个整数,以其作为索引向后查询最近符合正则的字符串
		while(matcher.find(i)){
			System.out.println(matcher.group());
			/*
			 * 1
			 * 2
			 * 2
			 * 5
			 * 5
			 * 5
			 */
			i++;
		}
	}

group

	public static void main(String[] args)  {
		Pattern pattern = Pattern.compile("\\d([a-z])");
		Matcher matcher = pattern.matcher("1a,2e");
		int count = matcher.groupCount();
		System.out.println(count); // 1
		while(matcher.find()){
			// 返回前一次匹配操作
			String string = matcher.group(); 
			System.out.println(string);
			// 返回前一次匹配擦作指定组的内容
			string = matcher.group(0);
			System.out.println(string);
			// 组的起始索引 组的结束索引+1
			System.out.println(matcher.start(0) +" "+matcher.end(0));
			string = matcher.group(1);
			System.out.println(string);
			/*
            1a
            1a
            0 2
            a
            2e
            2e
            3 5
            e
            */
		}

	}

匹配

public static void main(String[] args)  {
		Pattern pattern = Pattern.compile("\\d([a-z])");
		Matcher matcher = pattern.matcher("1a,2e");
		// System.out.println(matcher.group()); // 只有匹配后才可以使用
		// 字符串始符合时就成功
		boolean b = matcher.lookingAt();
		String string = matcher.group();
		System.out.println(string); // 1a
		// 只有全部符合时才成功
		b = matcher.matches();
		System.out.println(matcher.group()); //  java.lang.IllegalStateException
		// 可以在任意位置匹配(其含有find(i)方法)
		while(matcher.find()){
			System.out.println(matcher.group());
		}
	}

Pattern标记

以下标记来自Pattern中的常量。

编译标记效果
CANON_EQ启用规范等价。
CASE_INSENSITIVE启用不区分大小写的匹配。
COMMENTS模式中允许空白和注释。
DOTALL启用 dotall 模式。
LITERAL启用模式的字面值解析。
MULTILINE启用多行模式。
UNICODE_CASE启用 Unicode 感知的大小写折叠。
UNIX_LINES启用UNIX行模式。
public static void main(String[] args)  {
		Pattern pattern = Pattern.compile("java",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
		Matcher matcher = pattern.matcher("java Java sJAVA");
		while(matcher.find()){
			System.out.println(matcher.group());
            /*
            java
            Java
            JAVA
            */
		}
	}
13.6.5 split()
public static void main(String[] args)  {
		String[] strings = "1a2b3c".split("[0-9]");
		System.out.println(Arrays.toString(strings));
		// 最多分出两个
		strings = "1a2b3c".split("[0-9]", 2);
		System.out.println(Arrays.toString(strings));
	}
/*
[, a, b, c]
[, a2b3c]
*/
13.6.6 替换
	public static void main(String[] args)  {
		String string = "1a2b3c4d+++";
		System.out.println(string.replaceFirst("\\d", "*")); // *a2b3c4d
		System.out.println(string.replaceAll("[a-z]", "*")); // 1*2*3*4*
		
		Pattern pattern = Pattern.compile("\\d[a-z]");
		Matcher matcher = pattern.matcher(string);
		StringBuffer sb = new StringBuffer();
		int i = 0;
		while(matcher.find()){
			// 执行渐进式替换
			if (i < 2) {
				matcher.appendReplacement(sb, "*");
			}else {
				matcher.appendReplacement(sb, "#");
			}
			i++;
			System.out.println(sb.toString());
            /*
            *
            **
            **#
            **##
            */
		}
		// 将字符串剩余部分添加到缓冲区
		matcher.appendTail(sb);
		System.out.println(sb.toString()); // **##+++
	}
13.6.7 reset()

重置要匹配的字符串

public static void main(String[] args)  {
		Pattern pattern = Pattern.compile("\\d");
		Matcher matcher = pattern.matcher("a1b1");
		while(matcher.find()){
			System.out.println(matcher.group());
		}
		matcher.reset("2c3d");
		while(matcher.find()){
			System.out.println(matcher.group());
		}
	}
/*
1
1
2
3
*/

13.7 扫描输入

使用输入流

public static void main(String[] args) throws IOException {
		String s = "hello world \n24 172";
		StringReader sr = new StringReader(s);
		BufferedReader br = new BufferedReader(sr);
		System.out.println("打招呼:");
		// 按行读取数据
		String temp = br.readLine();
		System.out.println(temp);
		System.out.println("年龄:");
		temp = br.readLine();
		String[] strings = temp.split(" ");
		// 切割读入的数据
		System.out.println(strings[0]);
		System.out.println("身高:");
		System.out.println(strings[1]);
	}
/*
打招呼:
hello world 
年龄:
24
身高:
172
*/

使用scanner

public static void main(String[] args) throws IOException {
		String s = "hello world \n24 172.25";
		StringReader sr = new StringReader(s);
		BufferedReader br = new BufferedReader(sr);
		Scanner scanner = new Scanner(br);
		System.out.println("打招呼:");
		String temp = scanner.nextLine();
		System.out.println(temp);
		System.out.println("年龄:");
		int age = scanner.nextInt();
		System.out.println(age);
		System.out.println("身高:");
		double high = scanner.nextDouble();
		System.out.println(high);
	}

scanner可以接收InputStream/File/String/Readable对象。

Scanner定界符

默认使用空白符,可以通过正则表达式自定义定界符。

	public static void main(String[] args) throws IOException {
		Scanner scanner = new Scanner("12,32 , 44");
        // 空白符和,
		scanner.useDelimiter("\\s*,\\s*");
		while (scanner.hasNext()) {
			System.out.println(scanner.next());
            /*
            12
            32
            44
            */
		}
        // 获得定界符
		Pattern p = scanner.delimiter();
		System.out.println(p); // \s*,\s*
	}

正则表达式匹配

	public static void main(String[] args) throws IOException {
		Scanner scanner = new Scanner("12 3 e 4");
		String pattern = "[0-9]+";
		// 匹配下一个是否符合,到第3个时,不符合规则,就不继续了
		while(scanner.hasNext(pattern)){
            // 找到符合的并下移一位
			scanner.next(pattern);
			MatchResult m = scanner.match();
			String string = m.group(0);
			System.out.println(string);
            /*
            12
            3
            */
		}
	}

13.8 StringTokenizer

该类是1.0时就有的,不支持正则表达式,基本被废弃。

public static void main(String[] args)  {
		String string = "hello,world";
		StringTokenizer stringTokenizer = new StringTokenizer(string,",");
		while (stringTokenizer.hasMoreElements()) {
			Object object = (Object) stringTokenizer.nextElement();
			System.out.println(object);
            /*
            hello
            world
            */
		}
	}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值