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.
3 Réponses :
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);
Si cette expression n'était pas ' t désiré, vous pouvez modifier / changer vos expressions dans regex101.com .
Vous pouvez également visualiser vos expressions dans jex.im :
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);
([\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:
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);
([\S\s]*?)(?:@\s|$)
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
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 code>.
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.
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
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() + "\""); } }
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 i > , et vous n'avez pas besoin de la correspondance @ | \\ R
capturée, vous utilisez donc à la place un (?: X)
non- groupe de capture .
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