2015-04-25 39 views
6

我需要与标点符号被存储在相同的阵列词语的分割的字符串(在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] - "..." 

是否有任何优雅的解决方案?

+0

是他们必须在同一顺序的句子? – Yoda

+0

应该将'......'分割为'''''''.'还是'''? – Pshemo

+0

是''!''还是'!?'可能?如果是的话我们应该分割它吗? – Pshemo

回答

0

对于所有您要区分的标点符号,您可以使用“,”等字符来替换字符串,例如“,”。

在特定情况下 “......” 你可以这样做:

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

然后拆分。

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

+0

这是不正确的Java语法。它应该全部用双引号代替。 –

+0

这包含一个错误。正如所写的,它返回:'[在,之前,例子,类,派生,来自,...,]'。做最后一次替换一次将修复它,但更优雅的解决方案将最后一次替换更改为:'.replace(“。”,“...”): – CandiedOrange

+0

另请注意,这并没有做任何事情来分割逗号正常。 – CandiedOrange

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 
... 
+0

在我看来,这就是我一直在寻找的东西。 'Arrays.stream(split).forEach(System.out :: println);' Java 8的这种语法? – storojs72

+0

这也是我的第一个想法,但这不会正确处理'... ...'。另外,如果OP句子的格式不正确,就像'foo!bar'一样,我们将以'foo''!bar'结尾。实际上,有太多重要的细节没有正确回答。 – Pshemo

+0

查看@MichałKordas的回答 – storojs72

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)。
1

你可以试试用省略号代替第一三重点:

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

然后你可以离开它,因为它是或将其转换回由整个阵列上运行replace("…", "...")

+0

但这样我们不会得到结果三重点但省略号。那么在什么情况下会有字符串包含'...'和'...'?我们将无法看到最初有哪一个(可能有这种知识可能很重要的场景)。 – Pshemo

+0

关于第一个 - 请参阅我的答案中的最后一句。关于第二点,您可以随时用任何唯一的字符串甚至UUID替换它。 –

+0

好吧,用独特的字符串替换'...'可以工作(假设我们将为每个解析出的文本生成它,这对于这类问题在解决方案中似乎不太自然)。我同意这一点。无论如何,为了让你的答案正确,你还需要在''foo“栏之类的标点符号之后添加处理split *的机制,不仅要在''''in'foo |”'之前,还要在'“之后, | foo'。 – Pshemo

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 
1

现在我会说,最简单的,可能干净的方式来实现你想要的是把重点放在寻找你的阵列想要的数据,而不是找到将文字分开的地方。

我这样说是因为split介绍了很多的例如像问题:

  • split(" +|(?=\\p{Punct})");将拆分只在空间和标点字符,这意味着像"abc" def文本将被分割分成"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}将匹配...
  • \p{Punct}将匹配单个标点字符根据documentation这是!"#$%&'()*+,-./:;<=>[email protected][]^_`{|}~

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

  • [\S&&\P{Punct}]+将匹配的一个或多个字符,这是
    • \S不空格
    • &&
    • \P{Punct}不标点符号字符(\P{foo}\p{foo}否定)。

演示:

String sentence = "In (the) preceding examples, classes derived from..."; 
Pattern p = Pattern.compile("[.]{3}|\\p{Punct}|[\\S&&\\P{Punct}]+"); 
Matcher m = p.matcher(sentence); 
while(m.find()){ 
    System.out.println(m.group()); 
} 

输出:

In 
(
the 
) 
preceding 
examples 
, 
classes 
derived 
from 
... 
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 
... 
相关问题