【问题标题】:Loop through characters in a string循环遍历字符串中的字符
【发布时间】:2016-01-24 04:53:43
【问题描述】:

如何循环遍历字符串中的每个字符,然后将该字符设置为其他字符?除非当然有更好的方法来做我想做的事情。我正在创建一个 Bukkit 插件,它将根据它包含的内容更改聊天消息中的单词。这是我目前所拥有的:

for (String word : e.getMessage().split(" ") {
    if (wordList.contains(word)) {
        e.setMessage(e.getMessage.replaceAll(word, "*");
    }
}

但是,我希望能够设置单词中的每个字符,而不是设置整个单词。我尝试过这样的事情,但我的 IDE 不喜欢它。请注意,这是在上述代码的基础上构建的,并且在检查 wordList 是否包含该单词的范围内。

for (char c : word.toCharArray()) {
    // there are no available methods for editing the char c
}

如果有人能帮我解决这个问题,我们将不胜感激。

【问题讨论】:

    标签: java string for-loop replace char


    【解决方案1】:

    您可以使用java.util.regex.Pattern 类和匹配每个字符的正则表达式来替换每个字符。

    ArrayList<String> wordList = new ArrayList<String>();
    wordList.add("foo");
    wordList.add("carrots");
    
    String message = "The foo bar message about carrots";
    
    // use this class to match each character with the regex dot
    Pattern p = Pattern.compile(".", Pattern.DOTALL);
    // use to create the new message from the words (some replaced with asterisk)
    StringBuffer newMessage = new StringBuffer();
    // loop through each word
    for (String word : message.split(" ") ){
        // if it is in your list....
        if (wordList.contains(word)) {
            // add it to newMessage, but replaced by asterisk.
            newMessage.append(p.matcher(word).replaceAll("*"));
        } else {
            // add the unmodified word
            newMessage.append(word);
        }
        // add a space before we loop to the next word
        newMessage.append(" ");
    }
    // set the new message string with some words replaced
    message = newMessage.toString().trim();
    System.out.println(message);
    

    运行时会输出如下文本:

    关于胡萝卜的 foo bar 消息

    关于***的***吧消息

    更新 - 用星号替换禁用词的示例代码

    public static void main(String[] args) {
        // Your input string
        String message = "The foo bar message about carrots. Carrots suck so do parrots. Parrotsucker is partially masked. Carrots was already replaced.";
        System.out.println(message);
        
        // An array of words you want to mask
        ArrayList<String> wordList = new ArrayList<String>();
        wordList.add("foo");
        wordList.add("carrots");
        wordList.add("parrots");
        
        // Create a regex to match the banned words.... in this case it will be "foo|carrots|parrots", case insensitive
        String regex = Arrays.toString(wordList.toArray());
        regex = regex.substring(1, regex.length()-1).replaceAll(", ", "|");
        Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        System.out.println("Regex: " + p);
        
        // Keep track of the asterisks strings by length so we don't generate more than once
        Map<Integer, String> maskMap = new HashMap<Integer, String>();
        // Since we use replaceAll we might get a match more than once, so we can track and skip once that have already been handled
        Vector<String> replaced = new Vector<String>();
        // Find a list of banned words in the input message
        Matcher m = p.matcher(message);
        // Loop over each of the matches
        while (m.find()){
            // Get the text of each match
            String match = m.group();
            // Have we already replaced it in the message?
            if ( !replaced.contains(match) ){
                // This is what we will replace it with
                String mask = null;
                // See if we have a string the same length as the current match
                if ( maskMap.containsKey(match.length())) {
                    // If so, get it out of the map.
                    mask = maskMap.get(match.length());
                    System.out.println("Got mask from maskMap: " + mask);
                } else {
                    // No mask, so generate one and save it in the Map
                    StringBuffer maskBuffer = new StringBuffer("*");
                    while ( maskBuffer.length() < match.length() ){
                        maskBuffer.append("*");
                    }
                    mask = maskBuffer.toString();
                    maskMap.put(mask.length(), mask);
                    System.out.println("Generated new entry for maskMap: " + mask);
                }
                // Replace the matched banned word with the correct mask
                message = message.replaceAll(match, mask);
                // Track that we already replaced this word
                replaced.add(match);
                System.out.println((new StringBuffer("   Replaced '").append(match).append("' with '").append(mask).append("'")).toString());
            } else {
                System.out.println("Aready replaced: " + match);
            }
        }
        
        // The message with banned words masked.
        System.out.println(message);
        
        System.exit(0);
        
    }
    

    产生以下输出:

    The foo bar message about carrots. Carrots suck so do parrots. Parrotsucker is partially masked. Carrots was already replaced.
    Regex: foo|carrots|parrots
    Generated new entry for maskMap: ***
       Replaced 'foo' with '***'
    Generated new entry for maskMap: *******
       Replaced 'carrots' with '*******'
    Got mask from maskMap: *******
       Replaced 'Carrots' with '*******'
    Got mask from maskMap: *******
       Replaced 'parrots' with '*******'
    Got mask from maskMap: *******
       Replaced 'Parrots' with '*******'
    Aready replaced: Carrots
    The *** bar message about *******. ******* suck so do *******. *******ucker is partially masked. ******* was already replaced.
    

    【讨论】:

    • 我没用过也没听说过Pattern类,它到底是干什么的?您提供的代码有什么作用?我想了解我在代码中输入的内容,而不仅仅是复制粘贴。
    • 从高层次上看,答案并不多——Pattern 类用于将正则表达式与字符串进行匹配。在这种情况下,您使用点 (.) 来匹配所有字符,然后用星号替换每个出现的字符。再看看你的代码还有其他错误,我会更新我的答案。
    • @RoboticPlayer 我的答案随着一些样本的更新而更新。请记住,如果末尾有标点符号,这将不起作用,因为您只在空格上进行拆分 - 您可能应该首先删除标点符号,因为 "foo" != "foo"。
    • 好的,它有效。但是,每当玩家聊天时,它都会将消息放在最后。这可能没有意义,所以我将解释。因此,如果我在聊天中输入“hello”,它会打印为 hello。如果我然后输入“test”,它将打印“hello test”。我试图通过将我的editedMessage 字段(相当于你的newMessage 字段)设置为null 来解决这个问题,但现在每当我聊天时(第二次)它都会抛出一个NPE。它只会工作一次,然后必须重新加载服务器。这是我所拥有的:pastebin.com/uyJx9ZKv
    • @RoboticPlayer 您应该将StringBuffer editedMessage = new StringBuffer(); 移动到for 循环的上方,使其具有本地范围而不是全局范围,或者在for 循环上方调用editedMessage.setLength(0) 以删除当前内容 - 你必须重新初始化它,否则它仍然会有以前的输入。
    【解决方案2】:

    该代码使您打印更改 * 的每个字符,直到没有空格,所以如果您的单词是 banana,他将打印 ********

    char[] c= word.toCharArray();
    
        String newString ="";
        for (int i = 0; i < c.length; i++) {
                if(c[i] != ' ' ) newString += "*";
        }
        System.out.println(newString);
    

    编辑

    如果你也想改变一个setence,改变这些

    String word = "foo  woord";
        char[] c= word.toCharArray();
    
        String newString ="";
        for (int i = 0; i < c.length; i++) {
                if(c[i] != ' ' ) newString += "*";
                else newString += " ";
        }
        System.out.println(newString);
    

    答案: **** ******

    希望我能解决你的问题

    【讨论】:

    • 这只是将“test”替换为“*”,问题是如何到达“test”的“****”但“foo”的“***” .
    • 你也应该使用StringBuffer之类的来连接字符串,使用+=会创建很多垃圾对象供GC清理。
    • @RoboticPlayer,请检查我的答案!
    【解决方案3】:

    首先,您不能直接在 String 中修改任何字符,因为 String 是不可变的。

    或者,您可以在合并所需字符后创建一个新字符串。

    看看这段代码:

            String word = "Look";
            String modifiedWord = word.substring(0,1) + "***" + word.substring(word.length()-1);
            System.out.println(modifiedWord);
    

    如果您只需要在匹配的字符串主体内而不是在字符串的开头和结尾设置符号,您可以试试这个。

    Output: L***k
    

    你的代码可以修改如下:

       String modifiedWord = null;
        for (String word : e.getMessage().split(" ") {
            if (wordList.contains(word)) {
                modifiedWord = word.substring(0,1) + "***" + word.substring(word.length()-1);
                e.setMessage(e.getMessage.replaceAll(word, modifiedWord);
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-16
      • 2014-09-19
      • 2023-03-23
      • 2018-10-19
      • 1970-01-01
      • 2017-10-06
      相关资源
      最近更新 更多