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

Java里如何用StringTokenizer处理复杂分隔符字符串

2023-01-057.6k 阅读

Java 中 StringTokenizer 类概述

在 Java 编程中,经常会遇到需要处理字符串并根据特定分隔符将其拆分成多个部分的情况。StringTokenizer 类提供了一种方便的方式来完成这个任务。StringTokenizer 位于 java.util 包中,它允许根据一个或多个分隔符对字符串进行标记化(tokenization)。

从本质上来说,StringTokenizer 类通过在字符串中定位分隔符来确定标记(token)的边界。当创建 StringTokenizer 对象时,需要传入要处理的字符串以及分隔符。然后,通过调用 nextToken() 方法,可以逐个获取字符串中的标记。

基本使用

让我们先看一个简单的示例,假设我们有一个以逗号分隔的字符串,我们想将其拆分成各个部分。

import java.util.StringTokenizer;

public class BasicExample {
    public static void main(String[] args) {
        String str = "apple,banana,orange";
        StringTokenizer tokenizer = new StringTokenizer(str, ",");
        while (tokenizer.hasMoreTokens()) {
            String token = tokenizer.nextToken();
            System.out.println(token);
        }
    }
}

在上述代码中,我们首先创建了一个字符串 str,它包含以逗号分隔的水果名称。然后,我们创建了一个 StringTokenizer 对象,传入字符串 str 和分隔符 ","。通过 while 循环,在 tokenizer 还有更多标记时,调用 nextToken() 方法获取每个标记并打印出来。

运行这段代码,输出结果将是:

apple
banana
orange

构造函数

StringTokenizer 类有三个构造函数:

  1. StringTokenizer(String str):此构造函数使用默认的分隔符集(空格、制表符、换行符、回车符、换页符)对字符串 str 进行标记化。
  2. StringTokenizer(String str, String delim):这个构造函数允许我们指定分隔符 delim 来对字符串 str 进行标记化。
  3. StringTokenizer(String str, String delim, boolean returnDelims):此构造函数除了指定分隔符 delim 外,还通过 returnDelims 参数决定是否将分隔符作为标记返回。如果 returnDelimstrue,分隔符也会被当作标记返回;如果为 false,则分隔符仅用于分隔字符串,不会作为标记返回。

处理复杂分隔符字符串

多个字符作为分隔符

当分隔符不仅仅是单个字符,而是多个字符组成时,StringTokenizer 同样可以处理。例如,假设我们有一个字符串,其中的单词由 “###” 分隔。

import java.util.StringTokenizer;

public class MultipleCharDelimiterExample {
    public static void main(String[] args) {
        String str = "hello###world###java";
        StringTokenizer tokenizer = new StringTokenizer(str, "###");
        while (tokenizer.hasMoreTokens()) {
            String token = tokenizer.nextToken();
            System.out.println(token);
        }
    }
}

在这个例子中,我们将 “###” 作为分隔符传入 StringTokenizer 的构造函数。程序运行后,输出结果如下:

hello
world
java

包含多种分隔符

有时,字符串可能包含多种不同的分隔符。比如,一个字符串中可能既有逗号,又有分号作为分隔符。

import java.util.StringTokenizer;

public class MultipleDelimitersExample {
    public static void main(String[] args) {
        String str = "apple,banana;cherry,date";
        StringTokenizer tokenizer = new StringTokenizer(str, ",;");
        while (tokenizer.hasMoreTokens()) {
            String token = tokenizer.nextToken();
            System.out.println(token);
        }
    }
}

在上述代码中,我们将逗号和分号都作为分隔符传入构造函数。运行程序,输出结果为:

apple
banana
cherry
date

处理空标记

在某些情况下,分隔符可能连续出现,这就会导致产生空标记。StringTokenizer 默认会忽略连续分隔符之间的空标记。

import java.util.StringTokenizer;

public class EmptyTokenExample {
    public static void main(String[] args) {
        String str = "apple,,banana";
        StringTokenizer tokenizer = new StringTokenizer(str, ",");
        while (tokenizer.hasMoreTokens()) {
            String token = tokenizer.nextToken();
            System.out.println(token);
        }
    }
}

运行这段代码,输出结果为:

apple
banana

可以看到,中间连续的两个逗号之间的空标记被忽略了。

如果我们希望保留空标记,可以使用 String.split 方法,它返回一个字符串数组,并且会保留空标记。

public class KeepEmptyTokensWithSplit {
    public static void main(String[] args) {
        String str = "apple,,banana";
        String[] parts = str.split(",");
        for (String part : parts) {
            System.out.println(part);
        }
    }
}

运行上述代码,输出结果为:

apple

banana

复杂分隔符模式

在更复杂的场景中,分隔符可能遵循某种模式。例如,我们有一个字符串,其中的单词由一个或多个数字分隔。

import java.util.StringTokenizer;

public class PatternDelimiterExample {
    public static void main(String[] args) {
        String str = "hello123world45java";
        StringBuilder delimBuilder = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            delimBuilder.append(i);
        }
        String delimiters = delimBuilder.toString();
        StringTokenizer tokenizer = new StringTokenizer(str, delimiters);
        while (tokenizer.hasMoreTokens()) {
            String token = tokenizer.nextToken();
            System.out.println(token);
        }
    }
}

在这个例子中,我们构建了一个包含所有数字的字符串作为分隔符。运行程序后,输出结果为:

hello
world
java

与其他字符串拆分方法的比较

与 String.split 方法的比较

  1. 性能:在大多数情况下,StringTokenizer 的性能要优于 String.splitString.split 方法会返回一个字符串数组,这意味着它需要预先计算出所有的标记并分配足够的空间来存储它们。而 StringTokenizer 是按需获取标记,在处理大字符串时,这种方式更加节省内存。
  2. 空标记处理:如前文所述,StringTokenizer 默认忽略连续分隔符之间的空标记,而 String.split 会保留空标记。
  3. 灵活性String.split 方法可以接受正则表达式作为分隔符,这在处理复杂的分隔模式时非常强大。相比之下,StringTokenizer 只能接受普通字符串作为分隔符,灵活性稍差。
import java.util.StringTokenizer;

public class ComparisonExample {
    public static void main(String[] args) {
        String str = "apple,banana,orange";

        long startTime = System.currentTimeMillis();
        StringTokenizer tokenizer = new StringTokenizer(str, ",");
        while (tokenizer.hasMoreTokens()) {
            tokenizer.nextToken();
        }
        long endTime = System.currentTimeMillis();
        long tokenizerTime = endTime - startTime;

        startTime = System.currentTimeMillis();
        String[] parts = str.split(",");
        for (String part : parts) {
            // 这里不做实际操作,仅为比较消耗时间
        }
        endTime = System.currentTimeMillis();
        long splitTime = endTime - startTime;

        System.out.println("StringTokenizer 耗时: " + tokenizerTime + " 毫秒");
        System.out.println("String.split 耗时: " + splitTime + " 毫秒");
    }
}

运行上述代码,通常会发现 StringTokenizer 的耗时更短。

与 Scanner 类的比较

Scanner 类也可以用于从输入源(如字符串、文件等)读取标记。与 StringTokenizer 相比:

  1. 功能丰富度Scanner 类提供了更丰富的功能,它可以处理不同类型的输入源,并且支持正则表达式作为分隔符。例如,可以使用 Scanner 从文件中读取数据并按特定模式拆分。
  2. 易用性StringTokenizer 的使用相对简单直接,只专注于字符串的标记化。而 Scanner 类由于功能更复杂,使用起来可能需要更多的配置和操作。
import java.util.Scanner;

public class ScannerExample {
    public static void main(String[] args) {
        String str = "apple,banana,orange";
        Scanner scanner = new Scanner(str).useDelimiter(",");
        while (scanner.hasNext()) {
            String token = scanner.next();
            System.out.println(token);
        }
        scanner.close();
    }
}

在这个例子中,我们使用 Scanner 类对字符串进行标记化,通过 useDelimiter 方法设置分隔符。

实际应用场景

配置文件解析

在许多应用程序中,配置文件常使用特定的分隔符来存储多个值。例如,一个简单的配置文件可能如下:

database=mysql,username=root,password=123456

我们可以使用 StringTokenizer 来解析这个配置文件中的各个键值对。

import java.util.StringTokenizer;

public class ConfigFileParser {
    public static void main(String[] args) {
        String configLine = "database=mysql,username=root,password=123456";
        StringTokenizer outerTokenizer = new StringTokenizer(configLine, ",");
        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);
        }
    }
}

运行上述代码,输出结果为:

database : mysql
username : root
password : 123456

日志文件处理

日志文件中可能会包含各种信息,并且通常会使用特定的分隔符来区分不同的字段。例如,一个简单的日志记录可能如下:

2023-01-01 12:00:00 INFO Starting application

我们可以使用 StringTokenizer 来提取日志中的时间、日志级别和消息。

import java.util.StringTokenizer;

public class LogFileProcessor {
    public static void main(String[] args) {
        String logLine = "2023-01-01 12:00:00 INFO Starting application";
        StringTokenizer tokenizer = new StringTokenizer(logLine);
        String timestamp = tokenizer.nextToken();
        String logLevel = tokenizer.nextToken();
        StringBuilder messageBuilder = new StringBuilder();
        while (tokenizer.hasMoreTokens()) {
            messageBuilder.append(tokenizer.nextToken()).append(" ");
        }
        String message = messageBuilder.toString().trim();
        System.out.println("时间戳: " + timestamp);
        System.out.println("日志级别: " + logLevel);
        System.out.println("消息: " + message);
    }
}

运行这段代码,输出结果为:

时间戳: 2023-01-01
日志级别: INFO
消息: Starting application

注意事项

  1. 线程安全性StringTokenizer 不是线程安全的。如果在多线程环境中使用 StringTokenizer,可能会导致数据不一致的问题。在多线程场景下,建议使用其他线程安全的字符串处理方式。
  2. 分隔符转义StringTokenizer 不支持分隔符的转义。如果字符串中包含需要转义的分隔符,StringTokenizer 可能无法正确处理。在这种情况下,可能需要使用 String.split 方法并结合正则表达式来处理。

总结

StringTokenizer 类在处理复杂分隔符字符串时提供了一种简单且高效的方式。通过合理使用其构造函数和方法,我们可以轻松地对字符串进行标记化处理。尽管它在某些方面不如 String.splitScanner 类灵活,但在性能和简单性方面具有一定的优势。在实际编程中,根据具体的需求和场景,选择合适的字符串拆分方法是非常重要的。无论是配置文件解析、日志文件处理还是其他字符串处理任务,StringTokenizer 都能在合适的场景中发挥作用。同时,我们也需要注意其线程安全性和分隔符转义等问题,以确保程序的正确性和稳定性。通过深入理解 StringTokenizer 的原理和用法,开发者可以更加高效地处理字符串相关的任务,提升程序的质量和性能。在实际应用中,建议对不同的字符串处理方法进行性能测试和比较,以便在特定场景下选择最优的解决方案。同时,随着 Java 技术的不断发展,新的字符串处理工具和方法也可能会出现,开发者需要持续关注并学习,以跟上技术的步伐。在处理复杂分隔符字符串时,还可以考虑结合正则表达式、流操作等其他 Java 特性,进一步提升字符串处理的灵活性和效率。总之,熟练掌握 StringTokenizer 以及其他相关字符串处理方法,对于 Java 开发者来说是一项非常重要的技能。