更有效的方法是在一串单词中创建一个字符串

时间:2014-04-16 16:31:18

标签: java regex string performance replace

我正在创建一个应用程序,我将获取推文并将其存储在数据库中。我将有一个专栏的完整文本和另一个只有推文的文字将保留(我需要用词来计算以后最常用的词)。

我目前的工作方式是使用6种不同的.replaceAll()函数,其中一些函数可能会被触发两次。例如,我将有一个for循环来删除每个"#标签"使用replaceAll()

问题在于我将编辑每隔几分钟就会发送的数千条推文,我认为我这样做的方式效率不会太高。

我对此顺序的要求是什么(也在下面的评论中写下):

  1. 删除所有提及的用户名
  2. 删除所有RT(转推标志)
  3. 删除所有提及的主题标签
  4. 用空格替换所有断行
  5. 用单个空格替换所有双倍空格
  6. 删除空格以外的所有特殊字符
  7. 以下是简短且可编辑的示例:

    public class StringTest {
    
        public static void main(String args[]) {
    
            String text = "RT @AshStewart09: Vote for Lady Gaga for \"Best Fans\""
                    + " at iHeart Awards\n"
                    + "\n"
                    + "RT!!\n"
                    + "\n"
                    + "My vote for #FanArmy goes to #LittleMonsters #iHeartAwards"
                    + " htt…";
    
            String[] hashtags = {"#FanArmy", "#LittleMonsters", "#iHeartAwards"};
            System.out.println("Before: " + text + "\n");
    
            // Delete all usernames mentioned (may run multiple times)
            text = text.replaceAll("@AshStewart09", "");
            System.out.println("First Phase: " + text + "\n");
    
            // Delete all RT (retweets flags)
            text = text.replaceAll("RT", "");
            System.out.println("Second Phase: " + text + "\n");
    
            // Delete all hashtags mentioned
            for (String hashtag : hashtags) {
                text = text.replaceAll(hashtag, "");
            }
            System.out.println("Third Phase: " + text + "\n");
    
            // Replace all break lines with spaces
            text = text.replaceAll("\n", " ");
            System.out.println("Fourth Phase: " + text + "\n");
    
            // Replace all double spaces with single spaces
            text = text.replaceAll(" +", " ");
            System.out.println("Fifth Phase: " + text + "\n");
    
            // Delete all special characters except spaces 
            text = text.replaceAll("[^a-zA-Z0-9 ]+", "").trim();
            System.out.println("Finaly: " + text);
        }
    }
    

3 个答案:

答案 0 :(得分:3)

依赖replaceAll可能是最大的性能杀手,因为它一次又一次地编译正则表达式。使用正则表达式可能是第二个最重要的问题。

假设所有用户名都以@开头,我会替换

// Delete all usernames mentioned (may run multiple times)
text = text.replaceAll("@AshStewart09", "");

通过循环复制所有内容,直到找到@,然后检查以下字符是否与列出的任何用户名匹配,并可能跳过它们。对于此查找,您可以使用trie。一个更简单的方法是使用replaceAll类似的正则表达式#\w+循环以及HashMap查找。

// Delete all RT (retweets flags)
text = text.replaceAll("RT", "");

在这里,

private static final Pattern RT_PATTERN = Pattern.compile("RT");

肯定会赢。可以类似地处理以下所有部件。而不是

// Delete all special characters except spaces 
text = text.replaceAll("[^a-zA-Z0-9 ]+", "").trim();

你可以使用Guava的CharMatcher。方法removeFrom完全按照您的方式执行,但collapseFromtrimAndCollapseFrom可能会更好。

答案 1 :(得分:1)

根据现在关闭的question,这一切归结为

tweet = tweet.replaceAll("@\\w+|#\\w+|\\bRT\\b", "")
                .replaceAll("\n", " ")
                .replaceAll("[^\\p{L}\\p{N} ]+", " ")
                .replaceAll(" +", " ")
                .trim();

第二行似乎是多余的,因为第三行也会删除\n。将第一行的替换更改为" "不会更改允许聚合替换的结果。

tweet = tweet.replaceAll("@\\w*|#\\w*|\\bRT\\b|[^@#\\p{L}\\p{N} ]+", " ")
                .replaceAll(" +", " ")
                .trim();

我已将用户名和主题标签部分更改为仅使用#@,因此不需要由特殊字符部分使用。这对于!@AshStewart09等字符串的正确处理是必要的。

为了获得最佳性能,您肯定需要预编译模式。我还重新建议在第二部分使用Guava的CharMatcher。番石榴是巨大的(我想2 MB),但你肯定会在那里找到更多有用的东西。所以最后你可以得到

private static final Pattern PATTERN =
    Pattern.compile("@\\w*|#\\w*|\\bRT\\b|[^@#\\p{L}\\p{N} ]+");
private static final CharMatcher CHAR_MATCHER = CharMacher.is(" ");

tweet = PATTERN.matcher(tweet).replaceAll(" ");
tweet = CHAR_MATCHER.trimAndCollapseFrom(tweet, " ");

答案 2 :(得分:0)

你可以将所有被替换的东西内联到一个调用中,将所有被空格替换的东西替换为一个调用就像这样(也使用正则表达式来查找主题标签和用户名,因为这看起来更容易):

text = text.replaceAll("@\w+|#\w+|RT", "");
text = text.replaceAll("\n| +", " ");
text = text.replaceAll("[^a-zA-Z0-9 ]+", "").trim();