MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java里使用StringTokenizer进行字符串分割的技巧

2021-12-085.1k 阅读

Java 里使用 StringTokenizer 进行字符串分割的技巧

StringTokenizer 概述

在 Java 编程中,对字符串进行分割是一项常见的操作。StringTokenizer 类提供了一种方便的方式来将字符串按照特定的分隔符进行分割。它位于 java.util 包中,自 Java 1.0 起就已存在。

StringTokenizer 允许你通过指定一个或多个分隔符来分解字符串。与其他字符串分割方法(如 String.split())不同,StringTokenizer 是一个基于枚举(Enumeration)的方式来处理分割结果,并且在遍历分割后的子字符串时不需要创建数组。

构造函数

StringTokenizer 有多个构造函数,这使得它在不同场景下都能灵活应用。

以默认分隔符构造

StringTokenizer(String str)

这个构造函数使用默认的分隔符集对给定的字符串进行分割。默认的分隔符集是空格、制表符(\t)、换行符(\n)和回车符(\r)。

例如:

String sentence = "Hello world! How are you?";
StringTokenizer tokenizer = new StringTokenizer(sentence);
while (tokenizer.hasMoreTokens()) {
    System.out.println(tokenizer.nextToken());
}

在上述代码中,tokenizersentence 按照默认分隔符进行分割,然后通过 while 循环和 hasMoreTokens() 以及 nextToken() 方法依次输出每个分割后的子字符串。输出结果为:

Hello
world!
How
are
you?

以指定分隔符构造

StringTokenizer(String str, String delim)

此构造函数允许你指定一个分隔符字符串 delim。字符串 str 将根据 delim 中的字符进行分割。

例如:

String data = "apple,banana;cherry:date";
StringTokenizer tokenizer = new StringTokenizer(data, ",;:.");
while (tokenizer.hasMoreTokens()) {
    System.out.println(tokenizer.nextToken());
}

在这段代码中,data 字符串根据 ",;:." 这些分隔符进行分割。输出结果为:

apple
banana
cherry
date

以指定分隔符并控制是否返回分隔符构造

StringTokenizer(String str, String delim, boolean returnDelims)

这个构造函数除了允许指定分隔符外,还通过 returnDelims 参数控制是否将分隔符作为子字符串返回。

如果 returnDelimstrue,则分隔符也会被当作子字符串返回。例如:

String data = "apple,banana;cherry:date";
StringTokenizer tokenizer = new StringTokenizer(data, ",;:.", true);
while (tokenizer.hasMoreTokens()) {
    System.out.println(tokenizer.nextToken());
}

输出结果为:

apple
,
banana
;
cherry
:
date

如果 returnDelimsfalse,则分隔符不会被当作子字符串返回,这与第二个构造函数的行为类似。

常用方法

StringTokenizer 类提供了几个关键方法来处理分割后的子字符串。

hasMoreTokens()

boolean hasMoreTokens()

该方法用于检查 StringTokenizer 对象中是否还有更多的子字符串可供获取。如果还有子字符串,则返回 true,否则返回 false

在前面的代码示例中,我们使用 while (tokenizer.hasMoreTokens()) 这样的循环结构,就是依赖 hasMoreTokens() 方法来判断是否继续获取下一个子字符串。

nextToken()

String nextToken()

此方法返回 StringTokenizer 对象的下一个子字符串。如果没有更多子字符串,则抛出 NoSuchElementException

例如:

String text = "one two three";
StringTokenizer tokenizer = new StringTokenizer(text);
try {
    System.out.println(tokenizer.nextToken()); // 输出 "one"
    System.out.println(tokenizer.nextToken()); // 输出 "two"
    System.out.println(tokenizer.nextToken()); // 输出 "three"
    System.out.println(tokenizer.nextToken()); // 抛出 NoSuchElementException
} catch (NoSuchElementException e) {
    System.out.println("没有更多子字符串了");
}

countTokens()

int countTokens()

该方法返回 StringTokenizer 对象中剩余的子字符串的数量。注意,这个数量是调用该方法时的即时数量,调用 nextToken() 方法会改变这个数量。

例如:

String text = "a,b,c,d";
StringTokenizer tokenizer = new StringTokenizer(text, ",");
System.out.println("剩余子字符串数量: " + tokenizer.countTokens()); // 输出 4
tokenizer.nextToken();
System.out.println("剩余子字符串数量: " + tokenizer.countTokens()); // 输出 3

StringTokenizer 与 String.split() 的比较

虽然 String.split() 也是用于字符串分割的常用方法,但它与 StringTokenizer 有一些重要的区别。

返回结果类型

String.split() 方法返回一个字符串数组,将所有分割后的子字符串存储在数组中。例如:

String sentence = "Hello world! How are you?";
String[] parts = sentence.split(" ");
for (String part : parts) {
    System.out.println(part);
}

StringTokenizer 使用枚举(Enumeration)的方式逐个返回子字符串,不会一次性创建数组,在内存使用上相对更节省,特别是当字符串很长且分割后的子字符串数量很多时。

性能

在性能方面,StringTokenizer 在某些情况下可能比 String.split() 更高效。String.split() 会创建一个数组来存储所有分割后的子字符串,这涉及到数组的创建和内存分配。而 StringTokenizer 按需返回子字符串,避免了一次性创建数组带来的开销。

例如,在处理非常大的字符串时,StringTokenizer 的内存使用会更友好,因为它不需要一次性存储所有分割后的子字符串。

功能灵活性

String.split() 提供了一些高级特性,比如可以使用正则表达式作为分隔符。例如:

String data = "a,123,b,456,c,789";
String[] parts = data.split("\\d+");
for (String part : parts) {
    System.out.println(part);
}

在这个例子中,使用正则表达式 \\d+ 作为分隔符,将字符串按照连续的数字进行分割。StringTokenizer 则不支持直接使用正则表达式作为分隔符,它只能使用普通的字符序列作为分隔符。

在实际场景中的应用

解析简单配置文件

假设我们有一个简单的配置文件,每一行的格式为 key=value,并且不同的键值对之间用分号(;)分隔。我们可以使用 StringTokenizer 来解析这个配置文件。

配置文件内容如下(config.txt):

username=admin;password=123456;server=localhost

Java 代码如下:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.StringTokenizer;

public class ConfigParser {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("config.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                StringTokenizer outerTokenizer = new StringTokenizer(line, ";");
                while (outerTokenizer.hasMoreTokens()) {
                    String pair = outerTokenizer.nextToken();
                    StringTokenizer innerTokenizer = new StringTokenizer(pair, "=");
                    String key = innerTokenizer.nextToken();
                    String value = innerTokenizer.nextToken();
                    System.out.println(key + " : " + value);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这段代码中,首先使用外层的 StringTokenizer 按照 ; 分割每一行中的键值对,然后使用内层的 StringTokenizer 按照 = 分割每个键值对,从而得到键和值并输出。

处理 CSV 文件数据

CSV(Comma - Separated Values)文件是一种常见的数据存储格式,每行数据由逗号分隔的多个字段组成。

假设我们有一个简单的 CSV 文件(data.csv)内容如下:

John,Doe,30
Jane,Smith,25

我们可以使用 StringTokenizer 来读取并处理这个 CSV 文件的数据。

Java 代码如下:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.StringTokenizer;

public class CSVProcessor {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("data.csv"))) {
            String line;
            while ((line = br.readLine()) != null) {
                StringTokenizer tokenizer = new StringTokenizer(line, ",");
                String firstName = tokenizer.nextToken();
                String lastName = tokenizer.nextToken();
                int age = Integer.parseInt(tokenizer.nextToken());
                System.out.println("姓名: " + firstName + " " + lastName + ", 年龄: " + age);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此代码使用 StringTokenizer 按照逗号分割每一行数据,依次获取名字、姓氏和年龄,并进行相应的处理和输出。

注意事项

空字符串处理

当使用 StringTokenizer 分割字符串时,如果遇到连续的分隔符,StringTokenizer 会跳过中间的空字符串。例如:

String data = "a,,c";
StringTokenizer tokenizer = new StringTokenizer(data, ",");
while (tokenizer.hasMoreTokens()) {
    System.out.println(tokenizer.nextToken());
}

输出结果为:

a
c

String.split() 方法在这种情况下,如果使用 split(","),会返回包含空字符串的数组 ["a", "", "c"]。如果想要 StringTokenizer 也处理空字符串,可以在构造 StringTokenizer 时将 returnDelims 设置为 true,然后自行处理分隔符来识别空字符串。

线程安全性

StringTokenizer 不是线程安全的。如果在多线程环境中使用 StringTokenizer,可能会出现数据不一致的问题。如果需要在多线程环境中进行字符串分割,可以考虑使用线程安全的替代方案,或者对 StringTokenizer 的使用进行适当的同步处理。

例如,可以使用 synchronized 块来确保在多线程环境下 StringTokenizer 的安全使用:

String data = "a,b,c";
StringTokenizer tokenizer = new StringTokenizer(data, ",");
synchronized (tokenizer) {
    while (tokenizer.hasMoreTokens()) {
        System.out.println(tokenizer.nextToken());
    }
}

总结

StringTokenizer 是 Java 中一个方便的字符串分割工具,它在处理简单的字符串分割场景,特别是需要按需获取子字符串而不希望一次性创建数组的情况下,具有一定的优势。与 String.split() 相比,它在内存使用和性能方面可能更适合某些特定的应用场景。然而,在使用 StringTokenizer 时,需要注意其对空字符串的处理方式以及线程安全性问题。通过合理运用 StringTokenizer,可以更高效地处理字符串分割相关的任务,提高代码的可读性和性能。在实际项目中,应根据具体需求和场景来选择合适的字符串分割方法,无论是 StringTokenizer 还是 String.split(),亦或是其他第三方库提供的字符串处理工具。