如何从字符串中分离许多不同的单词(Java)

Posted

技术标签:

【中文标题】如何从字符串中分离许多不同的单词(Java)【英文标题】:How to separate many different words from a string (Java) 【发布时间】:2019-05-21 10:01:51 【问题描述】:

我一直在努力弄清楚如何从我正在从文件中读取的长度未知的字符串中获取一个长度未知的单词。字符串中我想要的单词总是用“。”分隔。和/或“&”,整个字符串被引号包围。例如:“.Word.Characters&Numeric&Letters.Typos&Mistypes。”我知道每个“。”的位置。和“&”以及它们出现的次数。

我想根据单词是否用“。”分隔,将单词输入数组 Example[i][j]。或“&”。所以“。”之间包含的单词。将被设置到数组的第 i 列,由“&”链接的单词到数组的 j 行。

输入字符串可以包含大量可变数量的单词。这意味着可能只有一个感兴趣的词,或者一百多个。

我更喜欢使用数组来解决这个问题。从我读过的内容来看,正则表达式会很慢,但是可以。 split() 也可能有效,但我想我必须事先知道要查找哪些词。

从此字符串:“.Word.Characters&Numeric&Letters.Typos&Mistypes。”我希望得到:(不用担心是行还是列)

[[字],[null],[null]],

[[字符],[数字],[字母]],

[[Typos],[Mistypes],[null]]

从此字符串“.Alpha.Beta.Zeta&Iota”。我希望得到:

[[Alpha],[null]],

[[Beta],[null]],

[[Zeta],[Iota]]

//NumerOfPeriods tells me how many word "sections" are in the string
//Stor[] is an array that holds the string index locations of "."
for(int i=0;i<NumberOfPeriods;i++)

    int length = Stor[i];
    while(Line.charAt(length) != '"')
    
        length++;
    
    Example[i] = Line.substring(Stor[i], length);

//This code can get the words separated by "." but not by "&"

//Stor[] is an array that holds all string index locations of '.'
//AmpStor[] is an array that holds all string index locations of '&'
int TotalLength = Stor[0];
int InnerLength = 0;
int OuterLength = 0;
while(Line.charAt(TotalLength) != '"')

    while(Line.charAt(OuterLength)!='.')
    
        while(Line.charAt(InnerLength)!='&')
        
            InnerLength++;
        
        if(Stor[i] > AmpStor[i])
        
            Example[i][j] = Line.substring(Stor[i], InnerLength);
        
        if(Stor[i] < AmpStor[i])
        
            Example[i][j] = Line.substring(AmpStor[i],InnerLength);
        
            OuterLength++;
    

//Here I run into the issue of indexing into different parts of the array i & j

【问题讨论】:

我首先考虑的不是尝试一步完成,而是可能通过多个步骤分解问题,每个步骤都在最后一步的结果范围内工作。另一个考虑因素可能是考虑使用一个或多个正则表达式 欢迎来到 Stack Overflow!寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括所需的行为、特定问题或错误重现它所需的最短代码在问题本身。没有明确问题陈述的问题对其他读者没有用处。请参阅:How to create a Minimal, Complete, and Verifiable example。 你上面的所有代码都可以放入:String test2 = ".Alpha.Beta.Zeta&Iota."; for (String s : test2.split("\\pP")) System.out.println(s); 考虑如何构建代码,因为它对于没有副作用的进一步开发至关重要。 这是作业题吗? 【参考方案1】:

这是我尝试解决问题的方法:

import java.util.*;
import java.util.stream.*;

public class StringSplitSplits 

    private static final String S1 = ".Word.Characters&Numeric&Letters.Typos&Mistypes.";
    private static final String S2 = ".Alpha.Beta.Zeta&Iota.";

    public static void main(String [] args) 

        String str = stripStartAndEndDots(S1);
        String [] ss = str.split("\\.");
        int maxLength = getMaxLength(ss);

        String [][] sss = Stream.of(ss)
                                .map(s -> s.split("&"))
                                .map(s -> Arrays.copyOf(s, maxLength))
                                .toArray(String[][]::new);
        Stream.of(sss).forEach(s -> System.out.println(Arrays.toString(s)));
    

    private static String stripStartAndEndDots(String input) 
        if (input.startsWith(".")) 
            input = input.substring(1);
        
        if (input.endsWith(".")) 
            input = input.substring(0, input.length()-1);
        
        return input;
    

    /*
     * Get max length of the arrays split on the "&" for each
     * string element of the input string array.
     */
    private static int getMaxLength(String [] input) 
        return Stream.of(input)
                        .map(s -> s.split("&"))
                        .mapToInt(ss -> ss.length)
                        .max()
                        .orElse(0);
    

输入:".Word.Characters&amp;Numeric&amp;Letters.Typos&amp;Mistypes." 输出:

[Word, null, null]
[Characters, Numeric, Letters]
[Typos, Mistypes, null]

输入:".Alpha.Beta.Zeta&amp;Iota." 输出:

[Alpha, null]
[Beta, null]
[Zeta, Iota]

【讨论】:

【参考方案2】:

如果我正确理解了这个问题,您希望将字符串分成由“。”分隔的子字符串。然后对于每个子字符串,将其分隔为以“&”分隔的子子字符串。如果是这样,那么我会使用split 方法:

List<List<String>> terms = Arrays.stream(input.split("\\."))
    .map(s -> Arrays.asList(s.split("\\&"))
    .collect(Collectors.toList());

如果您确实需要将其作为空填充数组返回:

String[][] result = new String[terms.size()][ terms.stream.mapToInt(List::size).max().getAsInt()];
IntStream.range(0, terms.size()).forEach(i ->
    IntStream.range(0, terms.get(i).size()).forEach(j -> 
        result[i][j] = terms.get(i).get(j)));

【讨论】:

【参考方案3】:

这就是我将如何解决您的问题(它与您的代码完全不同,但它有效)。

首先,去掉引号和前后的非单词字符。这可以使用replaceAll

String Formatted = Line.replaceAll( "(^\"[.&]*)|([.&]*\"$)", "" );

第一个参数中的正则表达式将匹配两端的双引号以及前导和尾随.s 和&amp;s。该方法将返回一个新字符串,其中匹配的字符被删除,因为第二个参数是一个空字符串(它替换为一个空字符串)。

现在您可以使用split 方法在每个. 处拆分此字符串。您只能在此调用之后定义输出数组:

String[] StringGroups = Formatted.split( "\\." );
String[][] Elements = new String[StringGroups.length][];

在点前使用转义的反斜杠 (\\) 表示它应该在 . 字符上拆分,因为此方法采用正则表达式(并且仅 . 在任何非换行符上拆分) .

现在使用相同的split 方法在每个&amp; 处拆分该数组中的每个字符串。将结果直接添加到您的 Elements 数组中:

// Loop over the array
int MaxLength = 0;
for( int i = 0; i < StringGroups.length; i ++ ) 
   String StrGroup = StringGroups[ i ];
   String[] Group = StrGroup.split( "&" );
   Elements[ i ] = Group;

   // Measure the max length
   if( Group.length > MaxLength ) 
       MaxLength = Group.length;
   

输入不需要\\,因为&amp; 只匹配&amp; 字符。现在您只需将数据填充到数组中。 MaxLength 变量用于将 null 值添加到您的数组中。如果您不想要它们,只需将它们删除即可。

但是,如果您想要 null 值,请遍历您的元素数组并将当前行复制到新数组中:

for( int i = 0; i < Elements.length; i ++ ) 
    String[] Current = Elements[ i ];
    String[] New = new String[ MaxLength ];

    // Copy existing values into new array, extra values remain null
    System.arraycopy( Current, 0, New, 0, Current.length );
    Elements[ i ] = New;

现在,Elements 数组正好包含您想要的内容。

这是完整的可执行代码:

public class StringSplitterExample 
    public static void main( String[] args ) 
        test( "\".Word.Characters&Numeric&Letters.Typos&Mistypes.\"" );
        System.out.println(); // Line between
        test( "\".Alpha.Beta.Zeta&Iota.\"" );
    

    public static void test( String Line ) 
        String Formatted = Line.replaceAll( "(^\"[.&]*)|([.&]*\"$)", "" );
        String[] StringGroups = Formatted.split( "\\." );
        String[][] Elements = new String[StringGroups.length][];

        // Loop over the array
        int MaxLength = 0;
        for( int i = 0; i < StringGroups.length; i ++ ) 
            String StrGroup = StringGroups[ i ];
            String[] Group = StrGroup.split( "&" );
            Elements[ i ] = Group;

            // Measure the max length
            if( Group.length > MaxLength ) 
                MaxLength = Group.length;
            
        

        for( int i = 0; i < Elements.length; i ++ ) 
            String[] Current = Elements[ i ];
            String[] New = new String[ MaxLength ];

            // Copy existing values into new array, extra values remain null
            System.arraycopy( Current, 0, New, 0, Current.length );
            Elements[ i ] = New;
        

        for( String[] Group : Elements ) 
            for( String String : Group ) 
                System.out.print( String );
                System.out.print( " " );
            
            System.out.println();
        
    

这个例子的输出:

字 null null 字符 数字 字母 错别字错别字 null 阿尔法空 测试版 null 泽塔

所以这行得通,您甚至不需要知道.&amp; 字符在您的字符串中的位置。 Java 会为您做到这一点。

【讨论】:

以上是关于如何从字符串中分离许多不同的单词(Java)的主要内容,如果未能解决你的问题,请参考以下文章

如何从查询字符串中分离“选择顶部 * x”?

如何从输入字符串中分离整数并将它们转换为 int 类型以允许对它们进行计算

如何从 PySpark DataFrame 的列中分离特定字符并使用它们形成一个新列?

从元素中分离多行文本

如何从它的消息中分离异常类型

从同一个累积事实表中分离和独立的计数