【问题标题】:How do I replace multiple substrings with different substrings?如何用不同的子字符串替换多个子字符串?
【发布时间】:2015-11-09 16:11:48
【问题描述】:

我从文本文件中获得了这行和弦。例如,

String chordLine = "C     G   Am  C";
String transposedChordLine;

接下来,我需要使用下面的类将chordLine 转置为新的transposedChordLine,使用两个参数,一个String 和弦和转置的整数增量。例如,transpose("C", 2) 将返回 D

 public class Transposer{
    private int inc;
    private static ArrayList<String> keysSharp;
    private static ArrayList<String> keysFlat;

    Transposer(){
        keysSharp = new ArrayList<String>(Arrays.asList("C", "C#", "D", "D#","E", "F","F#", "G","G#", "A","A#", "B"));
        keysFlat = new ArrayList<String>(Arrays.asList("C", "Db", "D", "Eb","E", "F","Gb", "G","Ab", "A","Bb", "B"));
    }

    public String transpose(String chord,int inc){

        this.inc = inc;

        String newChord;

        if(chord.contains("/")){
            String[] split = chord.split("/");
            newChord = transposeKey(split[0]) + "/" + transposeKey(split[1]);
        }else
            newChord = transposeKey(chord); 
        return newChord;
    }

    private String transposeKey(String key){ // C#m/D# must pass C#m or D#
        String nKey, tempKey;

        if(key.length()>1){ 
            nKey = key.substring(0, 2);
            }
        else{ nKey = key; }


        int oldIndex, newIndex;

        if(key.contains("b")){
            oldIndex = (keysFlat.indexOf(nKey)>-1) ? keysFlat.indexOf(nKey) : keysFlat.indexOf(similarKey(nKey));
            newIndex = (oldIndex + inc + keysFlat.size())%keysFlat.size();
            tempKey = keysFlat.get(newIndex);
            nKey = (key.length() < 3) ? tempKey : key.replace(nKey, tempKey);
                //(nKey + key.substring(nKey.length(), key.length()));
        }
        else if(key.contains("#")){
            oldIndex = (keysSharp.indexOf(nKey)>-1) ? keysSharp.indexOf(nKey) : keysSharp.indexOf(similarKey(nKey));
            newIndex = (oldIndex + inc + keysSharp.size())%keysSharp.size();
            tempKey = keysSharp.get(newIndex);
            nKey = (key.length() < 3) ? tempKey : key.replace(nKey, tempKey);
        }
        else{
            nKey = nKey.substring(0, 1);
            oldIndex = (keysSharp.indexOf(nKey)>-1) ? keysSharp.indexOf(nKey) : keysSharp.indexOf(similarKey(nKey));
            newIndex = (oldIndex + inc + keysSharp.size())%keysSharp.size();
            tempKey = keysSharp.get(newIndex);
            nKey = (key.length() < 2) ? tempKey : key.replace(nKey, tempKey);
        }



        return nKey;
    }


    private String similarKey(String nKey) {
        String newKey;
        switch(nKey){
        case "Cb":
            newKey = "B";
            break;
        case "Fb":
            newKey = "E";
            break;
        case "E#":
            newKey = "F";
            break;
        case "B#":
            newKey = "c";
            break;
        default:
            newKey = null;
        }
        return newKey;
    }
}

如何在不丢失空格的情况下替换 chordLine? 增加 2 应该有 transposedChordLine="D A Bm D"

这是我目前的尝试:

public static void main(String[] args) {

    String chordLine = "C      G            Am       C";
    String transposedChordLine;
    String normalize = chordLine.replaceAll("\\s+", " ");
    String[] split = normalize.split(" ");

    //System.out.println(normalize);


    Transposer tran = new Transposer();
    String[] temp = new String[split.length];

    for(int i=0 ; i<split.length ; i++){
        temp[i] = tran.transpose(split[i], 2);
        //System.out.println(split[i]);
        System.out.print(temp[i]);
    }

    transposedChordLine = chordLine.replaceAll([split], temp); //which is wrong

}

【问题讨论】:

  • 许多潜在的解决方案。例如:创建一个对象ChordLine,它将实例化一条和弦线。在此对象的构造函数中,您可以显式跟踪和弦n_in_i+1 之间的空白数量。然后,在转置时,您可以简单地替换输出中的空白ChordLine
  • 对不起,这不是关于你的问题,而是你的代码真的很糟糕。不要在构造函数中初始化static 成员。将这两个静态变量更改为List&lt;String&gt;,并直接使用Arrays.asList 对其进行初始化。此外,不要使用实例字段将参数传递给私有方法。如果inc 在调用transpose 时可能不同,则将其作为参数传递给私有方法。如果inc 始终相同,则将其传递给构造函数,而不是transpose 方法。
  • 大约一年前我开始使用 Java。感谢您的评论。

标签: java string replace


【解决方案1】:

您的标记化非常不寻常(保留分隔符),您可能想自己做。基本上,如果您看到与音符匹配的标记,请将其传递给转置器。否则,传递一个空格。使用 while 循环浏览笔记。这是执行此操作的代码:

private static final Transposer transposer = new Transposer();

public static void main(String[] args) {
  String chordLine = "C      G            Am       C";

  String transposed = transposeChordLine(chordLine);

  System.out.println(transposed);
}

private static String transposeChordLine(String chordLine) {
  char[] chordLineArray = chordLine.toCharArray();

  StringBuilder transposed = new StringBuilder();
  StringBuilder currentToken = new StringBuilder();

  int index = 0;
  while(index < chordLine.length()) {
    if(chordLineArray[index] == ' ') {
      transposed.append(' ');
      currentToken = processToken(transposed, currentToken);
    } else {
      currentToken.append(chordLineArray[index]);
    }
    index++;
  }

  processToken(transposed, currentToken);

  return transposed.toString();
}

private static StringBuilder processToken(StringBuilder transposed,
    StringBuilder currentToken) {
  if(currentToken.length() > 0) {
    String currentChord = currentToken.toString();
    String transposedChord = transposer.transpose(currentChord, 2);
    transposed.append(transposedChord);
    currentToken = new StringBuilder();
  }
  return currentToken;
}

注意:您的代码存在一些风格问题。您可以在字段本身中初始化常量和弦映射;通过在构造函数中这样做,您会覆盖它们,做不必要的工作并可能导致问题,尤其是在多线程代码中。只需在字段声明中内联即可。将它们包装在 Collections.unmodifiableList 中也很好,因此它们在您的代码运行时无法更改,因此更容易避免意外出错。

此外,您不应将inc 变量保存在字段中,只需将其作为参数传递即可。这样,如果您两次使用同一个对象,它不会保留状态,因此是线程安全的。我知道这些事情对您当前的计划无关紧要,但现在学习这些习惯是件好事。这是修改后的Transposer 类:

public class Transposer {
  private static final List<String> keysSharp = Collections.unmodifiableList(Arrays.asList("C", "C#", "D", "D#", "E",
        "F", "F#", "G", "G#", "A", "A#", "B"));
  private static final List<String> keysFlat = Collections.unmodifiableList(Arrays.asList("C", "Db", "D", "Eb", "E",
      "F", "Gb", "G", "Ab", "A", "Bb", "B"));

  public String transpose(String chord, int inc) {
    String newChord;

    if (chord.contains("/")) {
      String[] split = chord.split("/");
      newChord = transposeKey(split[0], inc) + "/" + transposeKey(split[1], inc);
    } else
      newChord = transposeKey(chord, inc);
    return newChord;
  }

  private String transposeKey(String key, int inc) { // C#m/D# must pass C#m or D#
    String nKey, tempKey;

    if (key.length() > 1) {
      nKey = key.substring(0, 2);
    } else {
      nKey = key;
    }

    int oldIndex, newIndex;

    if (key.contains("b")) {
      oldIndex = (keysFlat.indexOf(nKey) > -1) ? keysFlat.indexOf(nKey)
          : keysFlat.indexOf(similarKey(nKey));
      newIndex = (oldIndex + inc + keysFlat.size()) % keysFlat.size();
      tempKey = keysFlat.get(newIndex);
      nKey = (key.length() < 3) ? tempKey : key.replace(nKey, tempKey);
      // (nKey + key.substring(nKey.length(), key.length()));
    } else if (key.contains("#")) {
      oldIndex = (keysSharp.indexOf(nKey) > -1) ? keysSharp.indexOf(nKey)
          : keysSharp.indexOf(similarKey(nKey));
      newIndex = (oldIndex + inc + keysSharp.size()) % keysSharp.size();
      tempKey = keysSharp.get(newIndex);
      nKey = (key.length() < 3) ? tempKey : key.replace(nKey, tempKey);
    } else {
      nKey = nKey.substring(0, 1);
      oldIndex = (keysSharp.indexOf(nKey) > -1) ? keysSharp.indexOf(nKey)
          : keysSharp.indexOf(similarKey(nKey));
      newIndex = (oldIndex + inc + keysSharp.size()) % keysSharp.size();
      tempKey = keysSharp.get(newIndex);
      nKey = (key.length() < 2) ? tempKey : key.replace(nKey, tempKey);
    }

    return nKey;
  }

  private String similarKey(String nKey) {
    String newKey;
    switch (nKey) {
    case "Cb":
      newKey = "B";
      break;
    case "Fb":
      newKey = "E";
      break;
    case "E#":
      newKey = "F";
      break;
    case "B#":
      newKey = "c";
      break;
    default:
      newKey = null;
    }
    return newKey;
  }
}

【讨论】:

    【解决方案2】:

    这里是更短的解决方案(我将此方法添加到Transposer 类):

    public String transposeLine(String chordLine, int inc) {
        Pattern pattern = Pattern.compile("\\S+\\s*"); // can be moved to static final field
        Matcher matcher = pattern.matcher(chordLine);
        StringBuffer sb = new StringBuffer();
        while(matcher.find()) {
            String chord = matcher.group();
            String transposed = transpose(chord.trim(), inc);
            matcher.appendReplacement(sb, 
                String.format(Locale.ENGLISH, "%-"+chord.length()+"s", transposed));
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
    

    我正在使用正则表达式匹配器来创建新字符串。正则表达式匹配和弦名称以及之后的所有空格。为了确保替换具有相同的长度,我使用String.format 并提供格式字符串,如%-XXs,其中XX 是带空格的非转置和弦的长度。请注意,如果没有足够的空格,则结果行会变长。

    用法:

    public static void main(String[] args) {
        String chordLine = "C      G            Am       C";
    
        System.out.println(chordLine);
        for(int i=0; i<12; i++) {
            String result = new Transposer().transposeLine(chordLine, i);
            System.out.println(result);
        }
    }
    

    输出:

    C      G            Am       C
    C      G            Am       C
    C#     G#           A#m      C#
    D      A            Bm       D
    D#     A#           Cm       D#
    E      B            C#m      E
    F      C            Dm       F
    F#     C#           D#m      F#
    G      D            Em       G
    G#     D#           Fm       G#
    A      E            F#m      A
    A#     F            Gm       A#
    B      F#           G#m      B
    

    【讨论】:

      【解决方案3】:

      给定一个和弦线、转置器和一个用于转置的增量:

      String chordLine = "C      G            Am       C";
      Transposer tran = new Transposer();
      int offset = 2;
      

      要在保留空白的同时获得转置的和弦线,您可以在空白边界处使用regular expression lookaroundssplit,然后通过转置器有条件地处理生成的字符串,如下所示:

      String transposed = Arrays.stream(chordLine.split("((?<=\\s)|(?=\\s))")).map(  // use regex to split on every whitespace boundary
          str ->                                                                          // for each string in the split
              Character.isWhitespace(str.charAt(0))                                       // if the string is whitespace
                  ? str                                                                   // then keep the whitespace
                  : tran.transpose(str, offset)                                           // otherwise, it's a chord, so transpose it
      ).collect(Collectors.joining());                                                    // re-join all the strings together
      

      或者,如果您更喜欢 Java 7,请在迭代标记时使用 StringBuilder 构建转置的和弦线:

      StringBuilder sb = new StringBuilder();
      for (String str : chordLine.split("((?<=\\s)|(?=\\s))")) {
          sb.append(Character.isWhitespace(str.charAt(0)) ? str : tran.transpose(str, offset));
      }
      String transposed = sb.toString();
      

      【讨论】:

        猜你喜欢
        • 2011-09-01
        • 2017-12-06
        • 2022-01-22
        • 2016-09-10
        • 1970-01-01
        • 2014-05-18
        • 2016-12-03
        • 2021-06-13
        相关资源
        最近更新 更多