如何拆分字符串,包括标点符号?

时间:2015-04-25 22:16:47

标签: java

我需要拆分一个字符串(在Java中),标点符号与单词存储在同一个数组中:

String sentence = "In the preceding examples, classes derived from...";
String[] split = sentence.split(" ");

我需要拆分数组:

split[0] - "In"
split[1] - "the"
split[2] - "preceding"
split[3] - "examples"
split[4] - ","
split[5] - "classes"
split[6] - "derived"
split[7] - "from"
split[8] - "..."

有没有优雅的解决方案?

7 个答案:

答案 0 :(得分:2)

你需要环顾四周:

String[] split = sentence.split(" ?(?<!\\G)((?<=[^\\p{Punct}])(?=\\p{Punct})|\\b) ?");

环顾断言,但(重要的是)在匹配时消耗输入。

一些测试代码:

String sentence = "Foo bar, baz! Who? Me...";
String[] split = sentence.split(" ?(?<!\\G)((?<=[^\\p{Punct}])(?=\\p{Punct})|\\b) ?");
Arrays.stream(split).forEach(System.out::println);

输出;

Foo
bar
,
baz
!
Who
?
Me
...

答案 1 :(得分:1)

您可以先尝试用省略号替换三点:

    String sentence = "In the preceding examples, classes derived from...";
    String[] split = sentence.replace("...", "…").split(" +|(?=,|\\p{Punct}|…)");

之后您可以保持原样,或者通过在整个阵列上运行replace("…", "...")将其转换回来。

答案 2 :(得分:1)

我相信这种方法会做你想做的事情

public static List<String> split(String str) {
    Pattern pattern = Pattern.compile("(\\w+)|(\\.{3})|[^\\s]");
    Matcher matcher = pattern.matcher(str);
    List<String> list = new ArrayList<String>();
    while (matcher.find()) {
        list.add(matcher.group());
    }
    return list;
}

它会将一个字符串拆分为

  1. 连续的字符
  2. 省略号...
  3. 以空格分隔的任何其他内容
  4. 对于这个例子

    "In the preceding examples, classes.. derived from... Hello, World! foo!bar"
    

    列表将是

    [0] In
    [1] the
    [2] preceding
    [3] examples
    [4] ,
    [5] classes
    [6] .
    [7] .
    [8] derived
    [9] from
    [10] ...
    [11] Hello
    [12] ,
    [13] World
    [14] !
    [15] foo
    [16] !
    [17] bar
    

答案 3 :(得分:1)

现在我要说最简单也可能最干净的方法来实现你想要的是专注于在数组中找到你想要的数据,而不是找到分割文本的地方。

我这样说是因为split引入了许多问题,例如:

  • split(" +|(?=\\p{Punct})");只会在空格和 之前分割 标点字符,这意味着像"abc" def这样的文字会被拆分为{ {1}} "abc "。因此,您认为它不会在def"之后拆分

  • 以前的问题可以通过添加"abc之类的其他|(?<=\\p{Punct})条件轻松解决,但由于split(" +|(?=\\p{Punct})|(?<=\\p{Punct})"),我们仍然无法解决您的所有问题。因此,我们需要找出防止这些点...之间分裂的方法。

    • 要做到这一点,我们可以尝试从.|.|.中排除.并尝试单独处理它,但这会使我们的正则表达式相当复杂。
    • 其他方法可能是用一些唯一的字符串替换\p{Punct},在我们的...逻辑中添加此字符串,然后在结果数组中将其替换回split。但是这种方法还需要我们知道文本中永远不可能有哪些字符串,所以每次解析文本时我们都需要生成它。
  • 另一个可能的问题是,如果标点符号将是...之类的第一个字符,则pre-java-8正则表达式引擎将在结果数组的开头生成空元素。因此,在Java 7中,"上的"foo" bar字符串拆分将导致(?=\p{Punct)元素。要避免此问题,您需要添加[ , "foo, " bar]之类的正则表达式,以防止在字符串的开头拆分。

无论如何,这些解决方案看起来过于复杂。

因此,考虑使用(?!^)类中的split方法而不是find方法,而是关注结果数组中的内容。

尝试使用像这样的模式:Matcher

  • [.]{3}|\p{Punct}|[\S&&\P{Punct}]+"将匹配[.]{3}
  • ...会匹配单个标点字符,根据documentation\p{Punct}

    之一

    !"#$%&'()*+,-./:;<=>?@[]^_`{|}~ ! " # $ % & ' ( {{ 1}} ) * + , - . / : ; {{ 1}} < = > ? @ [ \ ] ^ {{ 1}} _ `

  • {将匹配一个或多个字符
    • |不是空白
    • }
    • ~没有标点字符([\S&&\P{Punct}]+是对\S的否定)。

演示:

&&

输出:

\P{Punct}

答案 4 :(得分:0)

你可以清理字符串替换,说&#34;,&#34;用&#34; ,&#34;等等,您需要区分所有标点符号。

在&#34; ...&#34;的特定情况下你可以这样做:

// there can be series of dots
sentence.replace(".", " .").replace(". .", "..")

然后你分手。

编辑:用双引号替换单引号。

答案 5 :(得分:0)

对于您的特定情况,两个主要挑战是排序(例如,第一个标点符号,然后是单词或其他方式)和...标点符号。

其余的你可以使用

轻松实现它
\p{Punct}
像这样:

Pattern.compile("\p{Punct}");

关于上述两个挑战:

1.Ordering: 您可以尝试以下方法:

private static final Pattern punctuation = Pattern.compile("\\p{Punct}");
private static final Pattern word = Pattern.compile("\\w");

public static void main(String[] args) {
    String sentence = "In the preceding examples, classes derived from...";
    String[] split = sentence.split(" ");
    List<String> result = new LinkedList<>();

    for (String s : split) {
        List<String> withMarks = splitWithPunctuationMarks(s);
        result.addAll(withMarks);
    }
}

private static void List<String> splitWithPunctuationMarks(String s) {
    Map<Integer, String> positionToString = new TreeMap<>();
    Matcher punctMatcher = punctuation.matcher(s);
    while (punctMatcher.find()) {
        positionToString.put(punctMatcher.start(), punctMatcher.group())
    }
    Matcher wordMatcher = // ... same as before
    // Then positionToString.values() will contain the 
    // ordered words and punctuation characters.
}
  1. ...每次找到时,您都可以尝试在(currentIndex - 1)处回顾以前出现的.字符。

答案 6 :(得分:0)

这里的另一个例子。这个解决方案可能适用于所有组合。

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class App {

    public static void main(String[] args) {    
        String sentence = "In the preceding examples, classes derived from...";
        List<String> list = splitWithPunctuation(sentence);
        System.out.println(list);
    }

    public static List<String> splitWithPunctuation(String sentence) {
        Pattern p = Pattern.compile("([^a-zA-Z\\d\\s]+)");
        String[] split = sentence.split(" ");
        List<String> list = new ArrayList<>();

        for (String s : split) {
            Matcher matcher = p.matcher(s);
            boolean found = false;
            int i = 0;
            while (matcher.find()) {
                found = true;
                list.add(s.substring(i, matcher.start()));
                list.add(s.substring(matcher.start(), matcher.end()));
                i = matcher.end();
            }

            if (found) {
                if (i < s.length())
                    list.add(s.substring(i, s.length()));
            } else
                list.add(s);
        }

        return list;
    }
}

输出:

In
the
preceding
examples
,
classes
derived
from 
...

一个更复杂的例子:

String sentence = "In the preced^^^in## examp!les, classes derived from...";
List<String> list = splitWithPunctuation(sentence);
System.out.println(list);

输出:

In
the
preced
^^^
in
##
examp
!
les
,
classes
derived
from
...