1
votes

L'utilisation du caractère d'espacement vertical comme délimiteur du scanner Java scanne la chaîne vide à chaque ligne de fin

J'essaie de numériser un fichier avec des champs de données délimités par «@» ou «endline» en utilisant la classe Scanner en Java. Voici un exemple de fichier d'entrée:

Token #1: Student
Token #2:  Codey 
Token #3:  Huntting
Token #4
Token #5: Student
Token #6:  Sarah
Token #7:  Honsinger
Token #8: 

Pour analyser correctement un fichier d'entrée qui ressemble à ceci, j'ai essayé de changer le délimiteur sur le scanner Java en l'expression régulière "[ @ \\ v] ", qui doit correspondre à @ ou à tout espace blanc vertical, y compris \ n et \ r selon cette page

Voici le code que j'utilise pour le tester:

Token #1: Student
Token #2:  Codey 
Token #3:  Huntting
Token #4: Student
Token #5:  Sarah
Token #6:  Honsinger

Les jetons que je compte analyser sont:

Scanner scanner = new Scanner(new File("data/initialize.txt"));
int tokenNum = 0;

scanner.useDelimiter("[@\\v]");

while(scanner.hasNext()) {
    System.out.println("Token #" + tokenNum++ + ": " + scanner.next());
}

scanner.close();

Mais les jetons réellement reçus sont:

Student @ Codey @ Huntting
Student @ Sarah @ Honsinger

Je me serais attendu à ce que le Scanner, après avoir scanné Huntting , monte à la nouvelle ligne après la chasse et, au prochain appel à input.next () , sautez cette nouvelle ligne, mais pour une raison quelconque, le scanner semble saisir une chaîne vide à la fin de la ligne .

J'ai vérifié plusieurs fois et le fichier ne contient aucun s fait les cent pas après l’une des lignes. J'ai essayé différents modèles tels que [@ [\\ v]] et [@] [\\ v] , mais ceux-ci donnent toujours des données soit avec la même chaîne vide erreur ou la sortie est complètement déséquilibrée.


2 commentaires

Vos fins de ligne sont probablement \\ r \\ n. Comme vous l'avez dit, \\ v est un caractère d'espacement vertical, mais \\ r \\ n sont deux d'entre eux. Ajoutez simplement un + à votre expression régulière pour réduire les séparateurs successifs: [@ \\ v] +


Alors je pourrais écrire [@ \\ v] + [\\ r \\ n] ? Je vais devoir essayer


3 Réponses :


0
votes

Si je comprends bien, nous pourrions vouloir simplement supprimer @ et un espace après cela, puis le remplacer par de nouvelles lignes et ajouter un texte avant. Peut-être que cette expression aiderait:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

final String regex = "([\\s\\S]+?)(@\\s|\\n\\s|\\n|$)";
final String string = "Student @ Codey @ Huntting\n\n"
     + "Student @ Sarah @ Honsinger                     \n";
final String subst = "Token #: $1\\n";

final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);

// The substituted value will be contained in the result variable
final String result = matcher.replaceAll(subst);

System.out.println("Substitution result: " + result);

entrez la description de l'image ici

RegEx

Si cette expression n'était pas ' t désiré, vous pouvez modifier / changer vos expressions dans regex101.com .

Circuit RegEx

Vous pouvez également visualiser vos expressions dans jex.im :

 entrez la description de l'image ici

Démo JavaScript

Cet extrait montre que nous avons probablement une expression valide:

const regex = /([\s\S]+?)(@\s|\n\s|\n|$)/gm;
const str = `Student @ Codey @ Huntting

Student @ Sarah @ Honsinger                     
`;
const subst = `Token #: $1\n`;

// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);

console.log('Substitution result: ', result);

Test Java

([\s\S]+?)(@\s|\n\s|\n|$)

Si nous souhaitons supprimer les nouvelles lignes, nous pourrions vouloir l'ajouter au deuxième groupe de capture, et au le problème pourrait être résolu:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

final String regex = "([\\S\\s]*?)(?:@\\s|$)";
final String string = "Student @ Codey @ Huntting\n"
     + "Student @ Sarah @ Honsinger";
final String subst = "$1\\n";

final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);

// The substituted value will be contained in the result variable
final String result = matcher.replaceAll(subst);

System.out.println("Substitution result: " + result);

Ici, dans le deuxième groupe de capture (@ \ s | \ n \ s | \ n | $) , en utilisant OU logique, nous pouvons exclure les caractères que nous ne souhaitons pas avoir:

entrez la description de l'image ici

Démo

const regex = /([\S\s]*?)(?:@\s|$)/gm;
const str = `Student @ Codey @ Huntting
Student @ Sarah @ Honsinger`;
const subst = `\n$1`;

// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);

console.log('Substitution result: ', result);

Test Java

([\S\s]*?)(?:@\s|$)


3 commentaires

Non, OP veut récupérer les jetons dans l'entrée. La boucle à imprimer est simplement un exemple réduit pour tester la logique Scanner.useDelimiter (...) , et nous apprécions tous un Exemple minimal et reproductible (anciennement appelé MCVE) .


Ah, d'accord, donc une stratégie consiste simplement à transformer le texte en quelque chose de plus facile à digérer et à le scanner de cette façon. Je pense que c'est un peu inefficace car nous devons opérer deux fois sur l'entrée réelle, plutôt que de simplement faire correspondre le modèle au fur et à mesure. Néanmoins, vous êtes clairement très bon avec les regex! Pouvez-vous penser à celui qui remplace les @ et \ n sans ces deux supplémentaires \ n?


Vous voyez, j'ai vu que \ R dans la documentation Java pour les expressions régulières, mais pour une raison quelconque, la classe Pattern a lancé une exception de syntaxe lorsque je l'ai essayé. Je ne sais pas ce qu'il y a là-haut



0
votes

Votre fichier contient probablement des sauts de ligne de la forme \ r \ n . Dans ce cas, votre scanner trouve le délimiteur \ r et affiche tout ce qui se trouve avant \ r . Ensuite, il trouve le délimiteur \ n et sort le jeton vide entre \ r et \ n , puis continue après \ n .

Pour permettre les coupures \ r \ n , je vous propose de prendre \ r \ n | [@ \ v] dans cet ordre exact comme regex de délimitation. Bien sûr, cela devient "\ r \ n | [@ \\ v]" après l'échappement.

Comme Andreas l'a mentionné, une autre expression régulière que vous pourriez utiliser est @ | \ R , car \ R correspond à n'importe quel saut de ligne Unicode, y compris \ r \ n ensemble. C'est probablement même la meilleure solution.


2 commentaires

J'ai remplacé cette expression régulière dans l'exemple de code et il a toujours donné la mauvaise sortie avec la chaîne vide. Je pense que vous avez raison sur le \ r \ n, cependant. Je ne sais tout simplement pas quelle expression régulière utiliser pour contourner ce problème


Puisque \ v correspond à \ r , il correspondra exactement à cela et s'arrêtera, donc vous obtenez toujours \ r \ n divisé en deux. Vous devez d'abord faire correspondre \ r \ n , c'est-à-dire \r\n|[@\v , bien que j'utilise \ R à la place, c'est-à-dire @|\R



0
votes

Votre problème est que le saut de ligne est une paire \ r \ n et que \ v les fait correspondre individuellement. Pour reproduire cela, modifions votre code pour utiliser une chaîne en ligne pour les données de test:

Token #0: "Student"
Token #1: "Codey"
Token #2: "Huntting"
Token #3: "Student"
Token #4: "Sarah"
Token #5: "Honsinger"

Sortie

useDelimiter("\\s*(?:@|\\R)\\s*")

Une façon de résoudre ce problème consiste à essayer d'abord de faire correspondre la paire \ r \ n :

useDelimiter("@|\\R")

Sortie

Token #0: "Student "
Token #1: " Codey "
Token #2: " Huntting"
Token #3: "Student "
Token #4: " Sarah "
Token #5: " Honsinger"

Cela passera cependant du temps à vérifier \ r deux fois, il serait donc peut-être préférable d'utiliser la fonction intégrée \ R ( Toute séquence de saut de ligne Unicode équivaut à \u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029ITED):

useDelimiter("\r\n|[@\\v]")

Même résultat, mais reflète plus clairement comment vous voulez faire correspondre.


Vous pouvez bien sûr utiliser trim () ou strip () pour supprimer les espaces de début et de fin, mais pourquoi ne pas demander à Scanner de faire le travail? L'utilisation de | nécessite un groupe (non capturant) pour le séparer des espaces blancs correspondants:

Token #0: "Student "
Token #1: " Codey "
Token #2: " Huntting"
Token #3: ""
Token #4: "Student "
Token #5: " Sarah "
Token #6: " Honsinger"
Token #7: ""

Output em >

String input = "Student @ Codey @ Huntting\r\n" +
               "Student @ Sarah @ Honsinger\r\n";
try (Scanner scanner = new Scanner(input).useDelimiter("[@\\v]")) {
    for (int tokenNum = 0; scanner.hasNext(); tokenNum++) {
        System.out.println("Token #" + tokenNum + ": \"" + scanner.next() + "\"");
    }
}


4 commentaires

Aaaaah je pense que je commence à voir! La seule chose que j'avais jamais essayée était [@ \\ v] | \\ r \\ n , mais il semble que l'ordre de tout cela gâchait. De plus, j'ai essayé le \\ R mais pour une raison quelconque, la classe Pattern a levé une exception. Je veux peut-être lui donner une autre chance. Aussi, A + pour savoir comment se débarrasser de l'espace blanc! C'était un peu ennuyeux de devoir écrire .trim () après chaque appel à .next ()


Magnifique! Juste magnifique! Même avec des lignes d'extrémité traînantes, cela fonctionne comme un charme! (Comme si je mets deux lignes de fin entre "Huntting" et "Student", le scanner ignore tout.) De plus, je dois avoir mal utilisé le \\ R parce que cela fonctionne très bien maintenant.


Hé, tu penses que tu pourrais m'expliquer ce dernier regex que tu as utilisé: \\ s * (?: @ | \\ R) \\ s * ? Je comprends le \\ s * et le @ | \\ R mais je ne comprends pas le ?: ou pourquoi tout le groupe est entre parenthèses


@CodeyHuntting L'utilisation abusive de \\ R a peut-être tenté de faire [@ \\ R] , car \\ R n'est pas un caractère classe (comme \\ s , \\ w , ...), de sorte que la compilation échouera. --- Si vous avez fait \\ s * @ | \\ R \\ s * , sans regroupement () , alors c'est comme (\\ s * @) | (\\ R \\ s *) , et ce n'est pas ce que vous voulez, alors vous faites \\ s * (@ | \\ R) \\ s * , mais c'est un groupe de capture , et vous n'avez pas besoin de la correspondance @ | \\ R capturée, vous utilisez donc à la place un (?: X) non- groupe de capture .