web-dev-qa-db-fra.com

Comment puis-je ajouter un caractère de nouvelle ligne pour toutes les lignes sauf la dernière?

Je parcours une HashMap (voir ma question précédente pour plus de détails) et construis une chaîne contenant les données contenues dans la carte. Pour chaque élément, j'aurai une nouvelle ligne, mais pour le dernier élément, je ne souhaite pas la nouvelle ligne. Comment puis-je atteindre cet objectif? Je pensais pouvoir vérifier si l'entrée est la dernière ou non, mais je ne sais pas comment faire.

Merci!

31
troyal

Changez votre processus de pensée de "ajouter un saut de ligne tout sauf la dernière fois" à "prévoir un saut de ligne tout sauf la première fois":

boolean first = true;
StringBuilder builder = new StringBuilder();

for (Map.Entry<MyClass.Key,String> entry : data.entrySet()) {
    if (first) {
        first = false;
    } else {
        builder.append("\n"); // Or whatever break you want
    }
    builder.append(entry.key())
           .append(": ")
           .append(entry.value());
}
70
Jon Skeet

une méthode (avec des excuses à Jon Skeet pour avoir emprunté une partie de son code Java):

StringBuilder result = new StringBuilder();

string newline = "";  
for (Map.Entry<MyClass.Key,String> entry : data.entrySet())
{
    result.append(newline)
       .append(entry.key())
       .append(": ")
       .append(entry.value());

    newline = "\n";
}
68
Joel Coehoorn

Et ça? 

StringBuilder result = new StringBuilder();

for(Map.Entry<MyClass.Key,String> entry : data.entrySet())
{
    builder.append(entry.key())
       .append(": ")
       .append(entry.value())
       .append("\n");
}

return builder.substring(0, builder.length()-1);

Excuses obligatoires et merci à Jon et Joel d'avoir "emprunté" à leurs exemples.

13
Jay R.

Habituellement, j'utilise Apache-commons-lang StringUtils # join . Bien qu'il ne soit pas vraiment difficile d'écrire toutes ces fonctionnalités, il est toujours préférable de réutiliser des bibliothèques éprouvées. Apache-commons est plein de choses utiles comme ça!

9
André

Si vous utilisez iterator au lieu de for ... chacun votre code pourrait ressembler à ceci:

StringBuilder builder = new StringBuilder();

Iterator<Map.Entry<MyClass.Key, String>> it = data.entrySet().iterator();

while (it.hasNext()) {
    Map.Entry<MyClass.Key, String> entry = it.next();

    builder.append(entry.key())
    .append(": ")
    .append(entry.value());

    if (it.hasNext()) {
        builder.append("\n");
    }
}
4
jan

C'est probablement un meilleur exemple ...

final StringBuilder buf = new StringBuilder();
final String separator = System.getProperty("line.separator"); // Platform new line
for (Map.Entry<MyClass.Key,String> entry: data.entrySet()) {
    builder.append(entry.key())
       .append(": ")
       .append(entry.value())
       .append(separator);
}
// Remove the last separator and return a string to use.
// N.b. this is likely as efficient as just using builder.toString() 
// which would also copy the buffer, except we have 1 char less 
// (i.e. '\n').
final String toUse = builder.substring(0, builder.length()-separator.length()-1);
3
Ben

Une solution consiste à créer un wrapper personnalisé pour StringBuilder. Il ne peut pas être étendu, un wrapper est donc requis.

public class CustomStringBuilder {

final String separator = System.getProperty("line.separator");

private StringBuilder builder = new StringBuilder();

public CustomStringBuilder appendLine(String str){
    builder.append(str + separator);
    return this;
}

public CustomStringBuilder append(String str){
    builder.append(str);
    return this;
}

public String toString() {
    return this.builder.toString();
}

}

Mise en œuvre comme celle-ci:

CustomStringBuilder builder = new CustomStringBuilder();

//iterate over as needed, but a wrapper to StringBuilder with new line features.

builder.appendLine("data goes here");
return builder.toString();

Cela a quelques inconvénients:

  • Écrire un code qui n'est généralement pas centré sur le "domaine/entreprise"
  • N'utilisez pas de solution standard open source telle que: StringUtils.join
  • Forcé de maintenir une classe qui encapsule une classe JDK finale et met donc à jour requis à long terme.

J'ai choisi la solution StringUtils.join pour effectuer une itération sur des collections et même créer des modèles de méthodes de construction légers dans le code.

2
Nick N

Voici ma version succincte, qui utilise la propriété length de StringBuilder au lieu d'une variable supplémentaire:

StringBuilder builder = new StringBuilder();

for (Map.Entry<MyClass.Key,String> entry : data.entrySet())
{
    builder.append(builder.length() > 0 ? "\n" : "")
           .append(entry.key())
           .append(": ")
           .append(entry.value());
}

(Toutes mes excuses et merci à Jon et Joel d'avoir "emprunté" leurs exemples.)

2
LukeH

En supposant que votre boucle foreach parcourt le fichier dans l'ordre, ajoutez simplement une nouvelle ligne à chaque chaîne et supprimez la dernière nouvelle ligne lorsque votre boucle se ferme.

1
Jared

Pas sûr que ce soit le meilleur, mais c’est le moyen le plus facile de le faire:

parcourez toutes les valeurs et ajoutez le\n normalement dans le tampon de chaîne. Alors, fais quelque chose comme ça

sb.setLength(sb.length()-1); 
1
Tiago

C’est là qu’une méthode de jointure, complémentaire de la scission, serait utile, car vous pourriez alors joindre tous les éléments en utilisant une nouvelle ligne comme séparateur, et bien sûr, elle n’ajoute pas de nouvelle ligne à la fin du résultat ; C'est comme ça que je le fais dans divers langages de script (Javascript, PHP, etc.).

0
George Jempty

Laissez les bibliothèques faire cela pour vous.

import com.Sun.deploy.util.StringUtils;

ainsi que beaucoup d'autres ont StringUtils, qui a une méthode de jointure. Vous pouvez le faire en une seule ligne:

StringUtils.join(list, DELIMITER);

Mais pour plus de contexte, voici comment vous pouvez le faire avec une hashmap.

public static String getDelimitatedData(HashMap<String, String> data) {
    final String DELIMITER = "\n"; //Should make this a variable
    ArrayList<String> entries = new ArrayList<>();

    for (Map.Entry<String, String> entry : data.entrySet()) {
        entries.add(entry.getKey() + ": " + entry.getValue());
    }

    return StringUtils.join(entries, DELIMITER);
}
0
Nick Humrich

Ha! Merci à ce post j’ai trouvé un autre moyen de le faire:

public static class Extensions
{
    public static string JoinWith(this IEnumerable<string> strings, string separator)
    {
        return String.Join(separator, strings.ToArray());
    }
}

Bien sûr, cela est en C # maintenant et Java ne supportera pas (encore) la méthode d’extension, mais vous devriez pouvoir l’adapter au besoin - l’essentiel est d’utiliser String.Join de toute façon, et je suis sûr que Java en a quelques-uns analogique pour cela.

Notez également que cela signifie qu’il faut effectuer une itération supplémentaire des chaînes, car vous devez d’abord créer le tableau, puis itérer dessus pour créer votre chaîne. De plus, vous allez créer le tableau où, avec d’autres méthodes, vous pourriez vous débrouiller avec un IEnumerable qui ne contient qu’une chaîne en mémoire à la fois. Mais j'aime beaucoup la clarté supplémentaire. 

Bien sûr, étant donné la capacité de la méthode d’extension, vous pouvez également résumer l’un des autres codes en une méthode d’extension.

0
Joel Coehoorn

Si vous utilisez Class Separator , vous pouvez faire 

StringBuilder buf = new StringBuilder();
Separator separator = new Separator("\n");
for (Map.Entry<MyClass.Key,String> entry: data.entrySet()) {
    builder.append(separator)
       .append(entry.key())
       .append(": ")
       .append(entry.value());
}

Le séparateur s'imprime en chaîne vide lors de sa première utilisation et le séparateur lors de toutes les utilisations ultérieures. 

0
akuhn

Utilisez les API de liaison de chaîne JDK:

String result = data.stream()
    .map((k,v) -> String.format("%s : %s", k, v)
    .collect(Collectors.joining("\n"));
0
Jan Nielsen