Solved Splitting a String

Discussion in 'Plugin Development' started by Theztc, Jun 1, 2021.

  1. Offline

    Theztc

    Hi. I have an idea for a TempMute command. I want the format to be /tempmute <player> <time> <reason>. My idea for doing the time to do the int and then a letter to indicate the time. For example /tempmute Theztc 1h <reason> would mute me for 1 hour. A day would be 1d, a second 1s, etc. Is there a way to split that arg between the Integer and the remainder of the String? Thanks!
     
  2. Offline

    Strahan

    I was curious about this, so I made it in my test plugin. Not sure if this is the best way to go about it, but it works good for me:
    Code:
    public class Test18 extends JavaPlugin implements Listener {
      Map<String, Integer> units = new HashMap<>();
    
      @Override
      public void onEnable() {
        getServer().getPluginManager().registerEvents(this, this);
        units.put("d", 86400);
        units.put("h", 3600);
        units.put("m", 60);
        units.put("s", 1);
      }
    
      @Override
      public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
        int duration = 0, tmp = 0; String reason = "";
        for (int x=0; x<args.length; x++) {
          if ((tmp = getDuration(args[x])) == -1) {
            reason = StringUtils.join(args, " ", x, args.length);
            sender.sendMessage("Mute for " + duration + " second(s) for: " + reason);
            return true;
          }
         
          duration += tmp;
        }
        return true;
      }
    
      private int getDuration(String val) {
        if (val.length() == 1) return -1;
        String unit = val.substring(val.length()-1);
        String dur = val.substring(0, val.length()-1);
        if (!units.containsKey(unit.toLowerCase())) return -1;
       
        try {
          int duration = Integer.parseInt(dur);
          return duration * units.get(unit.toLowerCase());
        } catch (NumberFormatException e) {
          return -1;
        }
      }
    
     
  3. Offline

    The_Spaceman

    This is about the same way I would have done it. But I'd change:

    Code:java
    1. private int getDuration(String val) {
    2. if (val.length() == 1) return -1;
    3. String unit = val.substring(val.length() - 1);
    4. String dur = val.substring(0, val.length() - 1);
    5.  
    6. try {
    7. int duration = Integer.parseInt(dur);
    8. for (HashMap.Entry<String, Integer> entries : units.entrySet()) {
    9. if (entries.getKey().equalsIgnoreCase(unit)) {
    10. return duration * units.get(unit.toLowerCase());
    11. }
    12. }
    13. } catch (NumberFormatException ignore) { }
    14.  
    15. return -1;
    16. }


    This way you arn't searching the map twice (once if it contains the unit, and then another time to get the unit)

    Or you can use someting like this:
    Code:java
    1. private int getDuration(String val) {
    2. if (val.length() == 1) return -1;
    3. String unit = val.substring(val.length() - 1);
    4. String dur = val.substring(0, val.length() - 1);
    5. Integer unitAmplification = units.getOrDefault(unit, null);
    6. if (unitAmplification != null) {
    7. try {
    8. int duration = Integer.parseInt(dur);
    9. return duration * unitAmplification;
    10. } catch (NumberFormatException ignore) { }
    11. }
    12. return -1;
    13. }
     
    Strahan likes this.
  4. Offline

    davidclue

    @The_Spaceman @Strahan Why don't you just split the string into an array of characters and then cycle through it adding the time based off the int and char something like this
    Code:
    private int convertToSeconds(String string) {
            int time = 0;
            String value = "";
            for (char c : string.toCharArray()) {
                if (Character.isDigit(c))
                    value += c;
                else {
                    if (c == 's')
                        time += Integer.parseInt(value);
                    else if (c == 'm')
                        time += Integer.parseInt(value) * 60;
                    else if (c == 'h')
                        time += Integer.parseInt(value) * 3600;
                    else if (c == 'd')
                        time += Integer.parseInt(value) * 86400;
                    value = "";
                }
            }
            return time;
        }
     
  5. Offline

    Theztc

    Very good idea here. I'll add this to my code and edit it on for anyone who visits after. Thanks guys!

    Edit: Here's how I did it. Didn't want to just copy and paste but it is very similar. Thanks again everyone!

    Code:
    Player player = Bukkit.getPlayer(args[0]);
    Integer time = 0;
    String timeAdding = "";
    String duration = "";
    for(char c : args[1].toCharArray()) {
        if(Character.isDigit(c)) {
            timeAdding += c;
        }else {
            if(c == 's') {
                time += Integer.parseInt(timeAdding);
                duration += timeAdding + " Seconds";
            }else if(c =='m') {
                time += Integer.parseInt(timeAdding) * 60;
                duration += timeAdding + " Minutes";
            }else if(c == 'h') {
                time += Integer.parseInt(timeAdding) * 3600;
                duration += timeAdding + " Hours";
            }else if(c == 'd') {
                time += Integer.parseInt(timeAdding) * 86400;
                duration += timeAdding + " Days";
            }           
        }
    }
    String reason = "";
    for(int i = 2; i < args.length; i++) {
        reason += args[i] + " ";
    }
    reason.trim();
    //Doing mute here
     
    Last edited: Jun 3, 2021
    davidclue likes this.
  6. Offline

    KarimAKL

    @Theztc @davidclue You should append strings using a StringBuilder when you are appending inside of a loop.

    Edited: Ignore this (open)
    Also, there is no need to parse the string to an integer when you are not doing calculations on it (I am talking about the seconds).
     
    Last edited: Jun 2, 2021
  7. Offline

    davidclue

    @KarimAKL How would you convert a string into an int without using parseInt?
     
  8. Offline

    KarimAKL

    @davidclue Right, ignore that part of my post. I need sleep. However, the StringBuilder point is still valid. :p
     
    davidclue likes this.
  9. Offline

    Theztc

    What is different about it? I'm unfamiliar with StringBuilder
     
  10. Offline

    KarimAKL

    @Theztc
    Source: https://docs.oracle.com/javase/8/docs/api/java/lang/String.html

    So what is happening when using String concatenation in a loop is something like this:
    Before compiling (open)
    Code:Java
    1. String[] toAdd = new String[] { "Some", " string", " here" };
    2. String string = "";
    3.  
    4. for (String str : toAdd) {
    5. string = string + str;
    6. }

    After compiling (open)
    Code:Java
    1. String[] toAdd = new String[] { "Some", " string", " here" };
    2. String string = "";
    3.  
    4. for (String str : toAdd) {
    5. string = new StringBuilder().append(string).append(str).toString();
    6. }

    So we are creating a new StringBuilder object for every iteration of the loop, which is bad.
    We can use a StringBuilder ourselves to avoid this.
    Code:Java
    1. String[] toAdd = new String[] { "Some", " string", " here" };
    2. StringBuilder stringBuilder = new StringBuilder();
    3.  
    4. for (String str : toAdd) {
    5. stringBuilder.append(str);
    6. }
    7.  
    8. String string = stringBuilder.toString();


    Note: This is not a problem outside of loops.
     
    Last edited: Jun 3, 2021
    davidclue and Theztc like this.
  11. Offline

    Theztc

    @KarimAKL
    I see, Thanks for explaining that. I'll do that in my code
     
    KarimAKL likes this.

Share This Page