Java基础 -- 字符串(格式化输出、正则表达式)

摘要:
以下是一个简单的示例:publicclassSimpleFormat{publicstaticvoidmain{intx=5;double=5.332542;System.out.println;//原始方法//格式输出方法System.out.format;System.out.pintf;}}输出如下:Row1:[55.332542]Row1:[5.5332542]Row1:+55.332542]您可以看到format()和printf()是等效的。它们只需要一个简单的格式字符串和一个参数字符串。每个参数对应一个格式修饰符。

一 字符串

1、不可变String

String对象是不可变的,查看JDK文档你就会发现,String类中每一个看起来会修改String值的方法,实际上都是创建一个全新的String对象,以包含修改后的字符串内容。而最初的String对象则没有改变。

看看下面的代码:

public class Immutable {
    public static String upcase(String s) {
        return s.toUpperCase();
    }
    
    public static void main(String[] args) {
        String q = "howdy";
        System.out.println(q);
        String qq = upcase(q);
        System.out.println(qq);
        System.out.println(q);
    }
} 

输出如下:

howdy
HOWDY
howdy 

当把q传递给upcase()方法时,实际传递的是引用的一个拷贝。其实,每当把String对象作为方法的参数时,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未改变。

回到upcase()的定义,传入其中的引用有了名字s,只有upcase()运行的时候,局部引用s才存在。一旦upcase()运行结束,s就消失了。当然了,upcase()的返回值,其实只是最终结果的引用。这足以说明,upcase()返回的引用已经指向了一个新的对象,而原本的q则还在原地。

String的这种行为其实正是我们想要的。例如:

String s = "asdf";
String x = Immutable.upcase(s);  

难道我们真的希望upcase()改变其参数吗?对于一个方法而言,参数是为该方法提供信息的,而不是想让该方法改变自己的。

2、StringBuilder

String的不可变性会带来一定的效率问题。以下面一段代码为例:

String str="abc";
System.out.println(str);
str=str+"de";
System.out.println(str);

如果运行这段代码会发现先输出“abc”,然后又输出“abcde”,好像是str这个对象被更改了,其实,这只是一种假象罢了,JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给str,然后在第三行中,其实JVM又创建了一个新的对象也名为str,然后再把原来的str的值和“de”加起来再赋值给新的str,而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了,所以,str实际上并没有被更改,也就是前面说的String对象一旦创建之后就不可更改了。所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。

为此Java引入了StringBuilder,StringBuilder对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度要比String快很多。

另外,有时候我们会这样对字符串进行赋值:

String str="abc"+"de";
StringBuilder stringBuilder=new StringBuilder().append("abc").append("de");
System.out.println(str);
System.out.println(stringBuilder.toString());

这样输出结果也是“abcde”和“abcde”,但是String的速度却比StringBuilder的反应速度要快很多,这是因为第1行中的操作和String str="abcde";是完全一样的,所以会很快,而如果写成下面这种形式:

String str1="abc";
String str2="de";
String str=str1+str2;

那么JVM就会像上面说的那样,不断的创建、回收对象来进行这个操作了。速度就会很慢。

总结一下:

  • String:适用于少量的字符串操作的情况;
  • StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况;
  • StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况;(后面会讲)

3、String对象的基本方法

  • 构造器 :默认版本、String、StringBuilder、StringBuffer、char数组、byte数组;
  • length():字符的个数;
  • charAt():获取String中指定索引位置上的char;
  • toCharArray():生成一个char[],包含String中的所有字符;
  • ....

二 格式化输出

 Java SE5推出了C语言中printf()风格的格式化输出这一功能。

1、System.out.format()

JAVA SE5引入的format()方法可用于PrintStream或PrintWriter对象,其中也包括System.out对象。format()方法模仿自C的printf()。如果你比较习惯使用printf()的话,你也可以使用System.out.printf()方法。

下面是一个简单的示例:

public class SimpleFormat {
    public static void main(String[] args) {
        int x = 5;
        double y = 5.332542;
        System.out.println("Row 1: [" + x + " " + y + "]");    //原来方式
        
        //格式化输出方式
        System.out.format("Row 1: [%d %f]
", x,y);
        System.out.printf("Row 1: [%d %f]
", x,y);
    }
}

输出如下:

Row 1: [5 5.332542]
Row 1: [5 5.332542]
Row 1: [5 5.332542]

可以看到,format()和printf()是等价的,他们只需要一个简单的格式化字符串,加上一串参数即可,每个参数对应一个格式修饰符。

2、Formatter类

在Java中,所有的字符串格式化功能都由java.util.Formatter类处理的。可以将Formatter看做一个翻译器,它将你的格式化字符串与数据翻译成需要的结果。当你创建一个Formatter对象的时候,需要向其构造器传递一些信息,告诉它最终的结果将向哪里输出

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)
",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);
    }
        
}

输出如下:

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)

所有的tommy都将输出到System.out,而所有的terry则输出到System.out的一个别名上,Formatter构造器经过重载可以接受多种输出目的地,不过最常用的还是PrintStream()、OutputStream和File。

3、格式化说明符

在插入数据时,如果想要控制空格与对齐,则需要使用更精细复杂的格式修饰符。以下是其抽象的语法:

%[argument_index$][flags][width][.precision]conversion
  • argument_index$:argument_index 是一个十进制整数,用于表明参数在参数列表中的位置。第一个参数由 “1$” 引用,第二个参数由 “2$” 引用,依此类推;
  • flags:可选 flags 是修改输出格式的字符集。有效标志集取决于转换类型;
  • width:控制一个域的最小值,默认情况下下是右对齐的,不过可以通过使用“-”标志来改变对其方向;
  • precision:精度,用于String时,表示输出字符的最大数量,用于浮点数时,表示小数部分要显示出来的位数(默认是6位),多则舍入,少则补0,用于整数会触发异常;
  • conversion:转换格式,可选的格式有:
d整数型(十进制)
Unicode字符
Boolean值
String
f浮点数(十进制)
e浮点数(科学计数)
X整数(十六进制)
h散列码(十六进制)
% 字符串“%”

注意当使用b作为转换格式时,即Boolean,对于boolean基本类型或者Boolean对象,其转换结果是对应的true或false。但是对于其他类型的参数,只要该参数不为null,那么该转换的结果就永远都是true。0也会转换为true的,跟其他语言有所区别。所以将b运用于非布尔类型要注意。

下面的程序应用格式修饰符来打印一个购物收据:

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
","Item","Qty","Price");
        f.format("%-15s %5s %10s
","---","---","-----");
    }
    
    
    public void print(String name,int qty,double price) {        
        //%-15.15s:字符串左对齐,最小长度为15,最大长度也为15
        //%10.2f:最小长度为10,其中小数占两位
        f.format("%-15.15s %5d %10.2f
",name,qty,price);
        total += price;
    }
    
    public void printTotal() {
        f.format("%-15s %5s %10.2f
","Tax","",total*0.06);
        f.format("%-15s %5s %10s
","","","-----");
        f.format("%-15s %5s %10.2f
","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();
    }
    
}

输出如下:

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

4、String.format()

Java SE5参考了C语言中的sprintf()方法,以生成格式化的String对象。String.format()是一个静态方法,它接受与Formatter.format()方法一样的参数,但是返回一个String对象。

下面演示一个String.format()的例子:

public class DatabaseException extends Exception{
    public DatabaseException(int transactionID,int queryID,String message) {
        //调用基类构造函数
        super(String.format("(t%d,q%d) %s", transactionID,queryID,message));
    }
    
    public static void main(String[] args) {
        try {
            throw new DatabaseException(3,7,"Write filed!");
        }catch(Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

输出如下:

(t3,q7) Write filed!

其实在String.format()的内部,也是创建了一个Formatter对象,然后将传入的参数转给该Formatter。不过,与其自己做这些事情,不如使用快捷的String.format()方法,何况这样的代码更清晰易懂。

三 正则表达式

正则表达式是一种强大而灵活的文本处理工具。

  • 正则表达式定义了字符串的模式;
  • 正则表达式可以用来搜索、编辑或处理文本;
  • 正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别;

1、正则表达式实例

一个字符串其实就是一个简单的正则表达式,例如 Hello World 正则表达式匹配 "Hello World" 字符串。

.(点号)也是一个正则表达式,它匹配任何一个字符如:"a" 或 "1"。

下表列出了一些正则表达式的实例及描述:

正则表达式描述

this is text

匹配字符串 "this is text"

thiss+iss+text

注意字符串中的 s+

匹配单词 "this" 后面的 s+ 可以匹配多个空格,之后匹配 is 字符串,再之后 s+ 匹配多个空格然后再跟上 text 字符串。

可以匹配这个实例:this is text

^d+(.d+)?

^ 定义了以什么开始

d+ 匹配一个或多个数字

? 设置括号内的选项是可选的

. 匹配 "."

可以匹配的实例:"5", "1.5" 和 "2.21"。

注意:

在其它语言中,\ 表示:我想要在正则表达式中插入一个普通的(字面上的)反斜杠,请不要给它任何特殊的意义。

在 Java 中,\ 表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。

所以,在其它的语言中(如Perl),一个反斜杠  就足以具有转义的作用,而在 Java 中正则表达式中则需要有两个反斜杠才能被解析为其它语言中的转义作用。也可以简单的理解在 Java 的正则表达式中,两个 \ 代表其他语言中的一个 ,因此,在Java中表示一位数字的正则表达式为\d,而表示一个普通的反斜杠为 \\。

Java 正则表达式和 Perl 的是最为相似的。

java.util.regex 包主要包括以下三个类:

  • Pattern 类:

    pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。比如:Pattern r = Pattern.compile("(\D*)(\d+)(.*)");

  • Matcher 类:

    Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。比如:Matcher m = r.matcher("This order was placed for QT3000! OK?");

  • PatternSyntaxException:

    PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。

以下实例中使用了正则表达式 .*runoob.* 用于查找字符串中是否包了 runoob 子串:

import java.util.regex.*;
 
class RegexExample1{
   public static void main(String args[]){
      String content = "I am noob " +
        "from runoob.com.";
 
      String pattern = ".*runoob.*";
 
      boolean isMatch = Pattern.matches(pattern, content);
      System.out.println("字符串中是否包含了 'runoob' 子字符串? " + isMatch);
   }
}

输出结果:

字符串中是否包含了 'runoob' 子字符串? true

2、捕获组

捕获组是把多个字符当一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。

例如,正则表达式 (dog) 创建了单一分组,组里包含"d","o",和"g"。

捕获组是通过从左至右计算其开括号来编号。例如,在表达式((A)(B(C))),有四个这样的组:

  • ((A)(B(C)))
  • (A)
  • (B(C))
  • (C)

可以通过调用 matcher 对象的 groupCount ()方法来查看表达式有多少个分组。groupCount() 方法返回一个 int 值,表示matcher对象当前有多个捕获组。

还有一个特殊的组(group(0)),它总是代表前一次匹配操作(例如find())的结果。该组不包括在 groupCount 的返回值中。

下面的例子说明如何从一个给定的字符串中找到数字串:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegexMatches
{
    public static void main( String args[] ){
 
      // 按指定模式在字符串查找
      String line = "This order was placed for QT3000! OK?";
      String pattern = "(\D*)(\d+)(.*)";
 
      // 创建 Pattern 对象
      Pattern r = Pattern.compile(pattern);
 
      // 现在创建 matcher 对象
      Matcher m = r.matcher(line);
      if (m.find( )) {
         System.out.println("Found value: " + m.group(0) );
         System.out.println("Found value: " + m.group(1) );
         System.out.println("Found value: " + m.group(2) );
         System.out.println("Found value: " + m.group(3) ); 
      } else {
         System.out.println("NO MATCH");
      }
   }
}

以上实例编译运行结果如下:

Found value: This order was placed for QT3000! OK?   //第一次匹配到的一个子字符串
Found value: This order was placed for QT            //第一个分组  
Found value: 3000                                    //第二个分组 
Found value: ! OK?                                   //第三个分组

3、正则表达式语法

字符

说明

将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,"n"匹配字符"n"。" "匹配换行符。序列"\\"匹配"\","\("匹配"("。

^

匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与" "或" "之后的位置匹配。

$

匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与" "或" "之前的位置匹配。

*

零次或多次匹配前面的字符或子表达式。例如,zo* 匹配"z"和"zoo"。* 等效于 {0,}。

+

一次或多次匹配前面的字符或子表达式。例如,"zo+"与"zo"和"zoo"匹配,但与"z"不匹配。+ 等效于 {1,}。

?

零次或一次匹配前面的字符或子表达式。例如,"do(es)?"匹配"do"或"does"中的"do"。? 等效于 {0,1}。

{n}

是非负整数。正好匹配 n 次。例如,"o{2}"与"Bob"中的"o"不匹配,但与"food"中的两个"o"匹配。

{n,}

是非负整数。至少匹配 次。例如,"o{2,}"不匹配"Bob"中的"o",而匹配"foooood"中的所有 o。"o{1,}"等效于"o+"。"o{0,}"等效于"o*"。

{n,m}

m 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,"o{1,3}"匹配"fooooood"中的头三个 o。'o{0,1}' 等效于 'o?'。注意:您不能将空格插入逗号和数字之间。

?

当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是"非贪心的"。"非贪心的"模式匹配搜索到的、尽可能短的字符串,而默认的"贪心的"模式匹配搜索到的、尽可能长的字符串。例如,在字符串"oooo"中,"o+?"只匹配单个"o",而"o+"匹配所有"o"。

.

匹配除" "之外的任何单个字符。若要匹配包括" "在内的任意字符,请使用诸如"[sS]"之类的模式。

(pattern)

匹配 pattern 并捕获该匹配的子表达式。可以使用 $0…$9 属性从结果"匹配"集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用"("或者")"。

(?:pattern)

匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用"or"字符 (|) 组合模式部件的情况很有用。例如,'industr(?:y|ies) 是比 'industry|industries' 更经济的表达式。

(?=pattern)

执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95|98|NT|2000)' 匹配"Windows 2000"中的"Windows",但不匹配"Windows 3.1"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

(?!pattern)

执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95|98|NT|2000)' 匹配"Windows 3.1"中的 "Windows",但不匹配"Windows 2000"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

x|y

匹配 x 或 y。例如,'z|food' 匹配"z"或"food"。'(z|f)ood' 匹配"zood"或"food"。

[xyz]

字符集。匹配包含的任一字符。例如,"[abc]"匹配"plain"中的"a"。

[^xyz]

反向字符集。匹配未包含的任何字符。例如,"[^abc]"匹配"plain"中"p","l","i","n"。

[a-z]

字符范围。匹配指定范围内的任何字符。例如,"[a-z]"匹配"a"到"z"范围内的任何小写字母。

[^a-z]

反向范围字符。匹配不在指定的范围内的任何字符。例如,"[^a-z]"匹配任何不在"a"到"z"范围内的任何字符。

匹配一个字边界,即字与空格间的位置。例如,"er"匹配"never"中的"er",但不匹配"verb"中的"er"。

B

非字边界匹配。"erB"匹配"verb"中的"er",但不匹配"never"中的"er"。

cx

匹配 x 指示的控制字符。例如,cM 匹配 Control-M 或回车符。x 的值必须在 A-Z 或 a-z 之间。如果不是这样,则假定 c 就是"c"字符本身。

d

数字字符匹配。等效于 [0-9]。

D

非数字字符匹配。等效于 [^0-9]。

f

换页符匹配。等效于 x0c 和 cL。

换行符匹配。等效于 x0a 和 cJ。

匹配一个回车符。等效于 x0d 和 cM。

s

匹配任何空白字符,包括空格、制表符、换页符等。与 [ f v] 等效。

S

匹配任何非空白字符。与 [^ f v] 等效。

制表符匹配。与 x09 和 cI 等效。

v

垂直制表符匹配。与 x0b 和 cK 等效。

w

匹配任何字类字符,包括下划线。与"[A-Za-z0-9_]"等效。

W

与任何非单词字符匹配。与"[^A-Za-z0-9_]"等效。

xn

匹配 n,此处的 n 是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,"x41"匹配"A"。"x041"与"x04"&"1"等效。允许在正则表达式中使用 ASCII 代码。

num

匹配 num,此处的 num 是一个正整数。到捕获匹配的反向引用。例如,"(.)1"匹配两个连续的相同字符。

n

标识一个八进制转义码或反向引用。如果 n 前面至少有 n 个捕获子表达式,那么 n 是反向引用。否则,如果 n 是八进制数 (0-7),那么 n是八进制转义码。

nm

标识一个八进制转义码或反向引用。如果 nm 前面至少有 nm 个捕获子表达式,那么 nm 是反向引用。如果 nm 前面至少有 n 个捕获,则 n 是反向引用,后面跟有字符 m。如果两种前面的情况都不存在,则 nm 匹配八进制值 nm,其中 和 m 是八进制数字 (0-7)。

nml

当 n 是八进制数 (0-3),m 和 l 是八进制数 (0-7) 时,匹配八进制转义码 nml

un

匹配 n,其中 n 是以四位十六进制数表示的 Unicode 字符。例如,u00A9 匹配版权符号 (©)。

根据 Java Language Specification 的要求,Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义。因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释。例如,当解释为正则表达式时,字符串字面值 "" 与单个退格字符匹配,而 "\b" 与单词边界匹配;字符串字面值 "(hello)" 是非法的,将导致编译时错误;要与字符串 (hello) 匹配,必须使用字符串字面值 "\(hello\)"。

4、Pattern.compile()

Pattern类的compile()方法除了只有一个正则表达式参数的版本,还有另一个版本:

Pattern Pattern.compile(String regex, int flag)

其中的flag来自以下Pattern类中的常量:

  • Pattern.CANON_EQ,当且仅当两个字符的"正规分解(canonical decomposition)"都完全相同的情况下,才认定匹配。比如用了这个标志之后,表达式"au030A"会匹配"?"。默认情况下,不考虑"规范相等性(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) 在这个模式下,只有' '才被认作一行的中止,并且与'.','^',以及'$'进行匹配。

在这些标记中,Pattern.CASE_INSENSITIVE、Pattern.MULTILINE以及Pattern.COMMENTS特别有用。

下面演示一个多个标志位组合的案例:

import java.util.regex.*;
public class RegexExample7 {
    public static void main(String[] args) {
        Pattern p = Pattern.compile("^java",Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);
        Matcher m = p.matcher(
                "java has regex
java has regex
"+
                "Java has pretty good regular expression
" + 
                "My name is Java"
                );
        while(m.find()) {
            System.out.println(m.group());
        }
    }
}

输出:

java
java
Java

在这个例子中,创建了一个模式,它将匹配所有以"java"、"Java"和“JAVA”等开头的行,并且是在设置了多行标记的状态下,对每一个行(从字符序列的第一个字符开始,至每一个行终结符)都进行匹配。注意:group()只返回已匹配的部分。

5、Matcher 类

(1) 索引方法

索引方法提供了有用的索引值,精确表明输入字符串中在哪能找到匹配:

序号方法及说明
1public int start() 
返回以前匹配的初始索引。
2public int start(int group)
 返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引
3public int end()
返回最后匹配字符之后的偏移量。
4public int end(int group)
返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。

下面是一个对单词 "cat" 出现在输入字符串中出现次数进行计数的例子:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegexMatches
{
    private static final String REGEX = "\bcat\b";
    private static final String INPUT =
                                    "cat cat cat cattie cat";
 
    public static void main( String args[] ){
       Pattern p = Pattern.compile(REGEX);
       Matcher m = p.matcher(INPUT); // 获取 matcher 对象
       int count = 0;
 
       while(m.find()) {
         count++;
         System.out.println("Match number "+count);
         System.out.println("start(): "+m.start());
         System.out.println("end(): "+m.end());
      }
   }
}

 以上实例编译运行结果如下:

Match number 1
start(): 0
end(): 3
Match number 2
start(): 4
end(): 7
Match number 3
start(): 8
end(): 11
Match number 4
start(): 19
end(): 22

可以看到这个例子是使用单词边界,以确保字母 "c" "a" "t" 并非仅是一个较长的词的子串。它也提供了一些关于输入字符串中匹配发生位置的有用信息。

Start 方法返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引,end 方法最后一个匹配字符的索引加 1。

(2) 研究方法

研究方法用来检查输入字符串并返回一个布尔值,表示是否找到该模式:

序号方法及说明
1public boolean lookingAt() 
 尝试将从区域开头开始的输入序列与该模式匹配。
2public boolean find() 
尝试查找与该模式匹配的输入序列的下一个子序列。
3public boolean find(int start)
重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。
4public boolean matches() 
尝试将整个区域与模式匹配。

matches 和 lookingAt 方法都用来尝试匹配一个输入序列模式。它们的不同是 matches 要求整个序列都匹配,而lookingAt 不要求。

lookingAt 方法虽然不需要整句都匹配,但是需要从第一个字符开始匹配。

这两个方法经常在输入字符串的开始使用。

我们通过下面这个例子,来解释这个功能:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegexMatches
{
    private static final String REGEX = "foo";
    private static final String INPUT = "fooooooooooooooooo";
    private static final String INPUT2 = "ooooofoooooooooooo";
    private static Pattern pattern;
    private static Matcher matcher;
    private static Matcher matcher2;
 
    public static void main( String args[] ){
       pattern = Pattern.compile(REGEX);
       matcher = pattern.matcher(INPUT);
       matcher2 = pattern.matcher(INPUT2);
 
       System.out.println("Current REGEX is: "+REGEX);
       System.out.println("Current INPUT is: "+INPUT);
       System.out.println("Current INPUT2 is: "+INPUT2);
 
 
       System.out.println("lookingAt(): "+matcher.lookingAt());
       System.out.println("matches(): "+matcher.matches());
       System.out.println("lookingAt(): "+matcher2.lookingAt());
   }
}

以上实例编译运行结果如下:

Current REGEX is: foo
Current INPUT is: fooooooooooooooooo
Current INPUT2 is: ooooofoooooooooooo
lookingAt(): true
matches(): false
lookingAt(): false

(3) 替换方法

替换方法是替换输入字符串里文本的方法:

序号方法及说明
1public Matcher appendReplacement(StringBuffer sb, String replacement)
实现非终端添加和替换步骤。
2public StringBuffer appendTail(StringBuffer sb)
实现终端添加和替换步骤。
3public String replaceAll(String replacement) 
 替换模式与给定替换字符串相匹配的输入序列的每个子序列。
4public String replaceFirst(String replacement)
 替换模式与给定替换字符串匹配的输入序列的第一个子序列。
5public static String quoteReplacement(String s)
返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement 方法一个字面字符串一样工作。

Matcher 类提供了appendReplacement 和 appendTail 方法用于文本替换:

看下面的例子来解释这个功能:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegexMatches
{
   private static String REGEX = "a*b";
   private static String INPUT = "aabfooaabfooabfoobkkk";
   private static String REPLACE = "-";
   public static void main(String[] args) {
      Pattern p = Pattern.compile(REGEX);
      // 获取 matcher 对象
      Matcher m = p.matcher(INPUT);
      StringBuffer sb = new StringBuffer();
      while(m.find()){
         m.appendReplacement(sb,REPLACE);
      }
      m.appendTail(sb);
      System.out.println(sb.toString());
   }
}

 以上实例编译运行结果如下:

-foo-foo-foo-kkk

replaceFirst 和 replaceAll 方法用来替换匹配正则表达式的文本。不同的是,replaceFirst 替换首次匹配,replaceAll 替换所有匹配。

下面的例子来解释这个功能:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegexMatches
{
    private static String REGEX = "dog";
    private static String INPUT = "The dog says meow. " +
                                    "All dogs say meow.";
    private static String REPLACE = "cat";
 
    public static void main(String[] args) {
       Pattern p = Pattern.compile(REGEX);
       // get a matcher object
       Matcher m = p.matcher(INPUT); 
       INPUT = m.replaceAll(REPLACE);
       System.out.println(INPUT);
   }
}

 以上实例编译运行结果如下:

The cat says meow. All cats say meow.

6、PatternSyntaxException 类的方法

PatternSyntaxException 是一个非强制异常类,它指示一个正则表达式模式中的语法错误。

PatternSyntaxException 类提供了下面的方法来帮助我们查看发生了什么错误。

 序号方法及说明
1 public String getDescription()
获取错误的描述。
2 public int getIndex()
 获取错误的索引。
3 public String getPattern()
获取错误的正则表达式模式。
4 public String getMessage()
返回多行字符串,包含语法错误及其索引的描述、错误的正则表达式模式和模式中错误索引的可视化指示。

参考文献

[1]Java中的String,StringBuilder,StringBuffer三者的区别

[2] Java 正则表达式(部分转载)

[3]Java编程思想

免责声明:文章转载自《Java基础 -- 字符串(格式化输出、正则表达式)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇centos7.5-docke安装(2020.9.1)MySQL复制日常维护与管理下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Dapper解析嵌套的多层实体类

在作项目的时候,我会将一些不涉及查询的字段,形成JSON统一存放在一个字段中,向下面这样的来建实体类, public class WechatModel { public string wechatid { get; set; } public WxMpModel wxmpinfo { get; set; } } p...

[工具]图片等比例压缩工具

写在前面 在网盘中有这样一个功能,需要获取所有图片的列表,想来想去,觉得还是生成相同比例的图片,在排版上更美观一些。所以就没事高了一个压缩的工具玩玩。 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Dat...

RxJava入门

项目小版本上线,抽空简单学习了下久仰大名的RxJava 一、引入 个人觉得rxjava的特点: 强大灵活的事件流处理(多线程/多事件/复合对象) 强大灵活优雅简洁的异步 链式调用 可自动Lambda化   实现:RxJava 是通过一种扩展的观察者模式来实现的 类比 类比 实际 实际 职责 演讲者 Button (可)被订阅者 (同右)...

Kubernetes (yaml 文件详解)

# yaml格式的pod定义文件完整内容:apiVersion: v1       #必选,版本号,例如v1kind: Pod       #必选,Podmetadata:       #必选,元数据  name: string       #必选,Pod名称  namespace: string    #必选,Pod所属的命名空间  labels:   ...

springmvc之文件上传、下载

1、接收到的是图片的流时 //上传头像 @RequestMapping(value = "/uploadHeadSculpture", method = RequestMethod.POST) @ResponseBody public String uploadHeadSculpture(@RequestParam("photo")...

ORM之轻量级框架--Dapper

转自:https://www.cnblogs.com/Erhao/p/10042808.html 一、什么是Dapper? Dapper是一款轻量级Orm框架,它是属于半自动的,它和Entity Framework和NHibernate不同,它只有一个单文件,没有很复杂的配置,如果你喜欢原生Sql语句,而且又是喜欢Orm框架,那么Dapper对于你来说是再...