Java 之路 (十三) -- 字符串(String、正则表达式)

版权声明:本文出自 whdAlive 的博客,转载必须注明出处 https://blog.csdn.net/whdAlive/article/details/81668759

个人吐槽:对于这章,第一遍读和重读之后果然看法不同:第一次读时觉得,文章用了大篇幅来介绍 API,这样我可以比较容易理解这个方法是干嘛的,以此理解这个类是干嘛的;但是重读之后觉得,文章居然用了这么大篇幅在讲 API(尤其是正则表达式),如果我需要知道具体某个方法怎么用,直接去官方文档查多好啊,这种情况下,我更希望得到某个方法可能出现的坑,而不只是方法的介绍(毕竟,这往往需要踩过坑之后总结的,hhh)。


1. String 基础

1.1 String 不可变

  • String 对象是不可变的。(只读性)

  • String 类中每一个修改 String 值的方法,实际上都是创建了一个新的 String 对象,原有的 String 对象丝毫未动。

1.2 String 的连接问题

“+” 和 “+=” 是经过 Java 重载之后的两个操作符,可用来对 String 拼接。

同时 Java 不允许程序员重载任何操作符。

String + String 与 StringBuilder.append() 对比

  1. 直接相加字符串(不含 String 引用,形如”hello”+”world”):效率最高,简单来说,其全部为常量,编译期就会优化为 “helloworld”,编译期值就已经确定了。

  2. 间接相加字符串(含 String 引用,如 str + “world”):效率最差,简单来说会取出原有 String 与 “world” 进行拼接,然后将结果存入另一个新的 String 对象中,虽然编译器进行了优化,通过 StringBuilder.append() 来实现拼接,但是当出现循环时,会出现 StringBuilder 对象的反复生成。

  3. StringBuilder.append():介于上述二者之间。

    综上:

    1. 如果是固定字符串如“xxx”,那么直接用 “+” 即可。
    2. 如果间接相加字符串,但是次数很少,则 StringBuilder 和 “+” 都可,差别不很大
    3. 如果简介相加字符串,且多次循环,那么强烈建议 StringBuilder

1.3 无意识的递归

当想要打印出某对象的内存地址时,应该调用 Object.toString() / super.toString() 方法,而非 this 上的 toString()。

换句话说,就是当前对象重写的 toString() 方法中,应该调用 super.toString() 而不是使用 this。

在当前对象的 toString() 方法中,如果调用 this,会发生自动类型转换,将对象类型转换为 String 类型。那么怎么转换呢?正式通过调用 this (当前对象)上的 toString() 方法。

em…完美的形成一个递归,自己调用自己,且没有终止条件。

1.4 String API

这里有想知道的一切 API

https://docs.oracle.com/javase/9/docs/api/java/lang/String.html

1.5 格式化输出

如下几种方式:

  1. System.out.println() 强行凑成指定形式,不推荐

  2. System.out.format():针对于 PrintStream 和 PrintWriter,其中包括 System.out 对象

  3. Formatter 类:可以看作是一个翻译器,将格式化字符串与数据翻译成需要的结果。

    1. 格式化说明符:%[argument_index$][flags][width][.precision]conversion

      width 指定域的最小尺寸

      precision 指定最大尺寸,适用于 String 和浮点数,无法用于整数

    2. 常用的类型转换

    类型转换字符 意义 类型转换字符 意义
    d 十进制整数型 e 科学计数浮点数
    c Unicode 字符 x 十六进制整数
    b Boolean 值 h 十六进制散列码
    s String % 字符 “%”
    f 十进制浮点数

    更多详见 https://docs.oracle.com/javase/9/docs/api/java/util/Formatter.html

  4. String.format()

    1. 实际上就是内部封装了 Formatter 对象,然后将传入的参数传递给 Formatter,由它来进行具体的处理。

2. 正则表达式

2.1 通用正则表达式

就不整理出来了,放上一篇教程

http://www.runoob.com/regexp/regexp-tutorial.html

https://docs.oracle.com/javase/9/docs/api/java/util/regex/Pattern.html

补充一点:

  • 在 java 中,”\\” 表示一个有效的 “\”,因此如果需要表示正则表达式里的一个数字 “\d”,需要写成 \”\\d”

2.2 Pattern & Matcher

接口 CharSequence 从 CharBuffer、String、String Buffer、StringBuilder 类之中抽象出了字符序列的一般化定义:

interface CharSequence {
    charAt(int i);
    length();
    subSequence(int start,int end);
    toString();
}

本小节简单介绍 Java 中如何处理正则表达式:

2.2.1 基础使用:

step 1:Pattern.compile(regex) 编译 String 类型的 regex,并产生 Pattern 对象

step 2:Pattern.matcher(待检索的字符串)生成一个 Matcher 对象

直接上例子,通过例子说明:

import java.util.regex;

public class TestRegularExpression {
    public static void main(String[] args) {
        String[] array = {"aabbcc", "aab", "aab+", "(b+)"};

        for (String arg : array) {
            System.out.println();
            print("Regular expression: \"" + arg + "\"");
            Pattern p = Pattern.compile(arg); // step1: Pattern 表示编译后的匹配模型Pattern.(编译后的正则表达式)
            Matcher m = p.matcher("aabbcc"); // step2: 模型实例 检索 待匹配字符串并 生成一个匹配对象Matcher, Matcher有很多方法
            while (m.find()) {
                print("Match \"" + m.group() // 待匹配的字符串
                                 + "\" at positions " 
                                 + m.start() // 字符串匹配regex的起始位置
                                 + "-" + (m.end() - 1)); // 字符串匹配regex的终点位置
            }
        }
    }

Pattern 对象表示编译后的正则表达式,重点在于 Matcher 对象,它提供了一系列方法来进行正则的匹配,下面简单介绍:

详见 https://docs.oracle.com/javase/9/docs/api/overview-summary.html

  1. Matcher.find():用来在 CharSequence 查找多个匹配

  2. Matcher.group():用来获取与组相关的信息

    组是用括号划分的正则表达式,可以用组的编号来引用某个组,组0表示整个表达式,组1表示被第一队括号括起来的组。。。

    A(B(C))D : 组0是 ABCD,组1是B,组2是C

  3. Matcher.start() & end():返回先前匹配的起始和截止位置的索引。

  4. Pattern 标记:重载方法,Pattern.compile(String regex, int flags) 接受一个 flags 参数,来调整匹配的行为。具体看官方文档:

    https://docs.oracle.com/javase/9/docs/api/java/util/regex/Pattern.html

  5. split():将输入字符串断开成字符串对象数组。

  6. 替换 replacexxx():替换文本

  7. reset():将现有的 Matcher 对象应用于一个新的字符序列。

3. 扫描输入

Scanner 是 Java SE5 中添加的特性,主要就是减轻扫描输入的工作负担,最实用的是获取控制台输入,其他比如从文件读取内容的,emmm…感觉有些鸡肋。

举个使用的简单例子:

public class SimpleScanner { 
        public static void main(String[] args) { 
                Scanner s = new Scanner(System.in); 
                System.out.println("请输入字符串:"); 
                while (true) { 
                        String line = s.nextLine(); 
                        if (line.equals("exit")) break; 
                        System.out.println(">>>" + line); 
                } 
        } 
}

/*
请输入字符串:
whdalive
>>>whdalive
exit

Process finished with exit code 0
*/

Scanner 使用很方便,这是因为它的构造器可以接受任何类型的输入对象,有了 Scanner 之后,所有输入、分词以及翻译的操作都隐藏在不同类型的 next 方法中。

3.1 Scanner 定界符

默认情况下,Scanner 根据空白字符对输入进行分词,但是我们可以用正则表达式指定自己所需的定界符。

public static void main(String[] args) throws FileNotFoundException { 
    Scanner s = new Scanner("12,42,78,99,42"); 
    s.useDelimiter(","); 
    while (s.hasNext()) { 
        System.out.println(s.next()); 
    } 
}
/*输出
12
42
78
99
42
*/

我们通过 useDelimiter() 方法来指定定界符,显然上述代码中我们使用的是逗号。

3.2 正则表达式扫描

Scanner 的 next 方法中,有一个重载方法可以接收 String 的正则表达式,此时它会找到下一个匹配该模式的输入部分,然后调用 match() 方法就可以获得匹配的结果。工作方式和正则表达式匹配是类似的。

但是需要注意一点:它仅仅针对下一个输入分词进行匹配,如果正则表达式中有定界符,那么永远不可能匹配成功。

猜你喜欢

转载自blog.csdn.net/whdAlive/article/details/81668759
今日推荐