web-dev-qa-db-fra.com

Analyse de fichier texte en Java

Je lis dans un fichier texte en utilisant FileInputStream qui met le contenu du fichier dans un tableau d'octets. Je convertis ensuite le tableau d'octets en chaîne en utilisant une nouvelle chaîne (octet). 

Une fois que j'ai la chaîne, j'utilise String.split("\n") pour diviser le fichier en un tableau de chaînes, puis je prends ce tableau de chaînes et nous l'analysons en faisant un String.split(",") et en conservant le contenu dans une liste de contrôle. 

J'ai un fichier 200 Mo + et il manque de mémoire lorsque je lance la machine virtuelle Java avec 1 Go de mémoire. Je sais que je dois faire quelque chose correctement quelque part, je ne suis tout simplement pas sûr si la façon dont je suis analysé est incorrecte ou la structure de données que j'utilise. 

Il me faut également environ 12 secondes pour analyser le fichier, ce qui semble être beaucoup de temps. Quelqu'un peut-il indiquer ce que je peux faire qui me fait manquer de mémoire et ce qui peut ralentir mon programme?

Le contenu du fichier se présente comme suit:

"12334", "100", "1.233", "TEST", "TEXT", "1234"
"12334", "100", "1.233", "TEST", "TEXT", "1234"
.
.
.
"12334", "100", "1.233", "TEST", "TEXT", "1234"

Merci

12
brock

On dirait que vous faites quelque chose de mal pour moi - une création d'objet entier.

Quelle est la représentativité de ce fichier "test"? Que faites-vous vraiment avec ces données? Si c'est typique de ce que vous avez réellement, je dirais qu'il y a beaucoup de répétition dans ces données.

Si tout est dans Strings, commencez par BufferedReader pour lire chaque ligne. Pré-allouez cette liste à une taille proche de celle dont vous avez besoin afin de ne pas gaspiller de ressources en y ajoutant à chaque fois. Découpez chacune de ces lignes à la virgule. veillez à enlever les guillemets.

Vous voudrez peut-être vous demander: "Pourquoi ai-je besoin de tout ce fichier en mémoire en même temps?" Pouvez-vous lire un peu, travailler un peu et ne jamais tout garder en mémoire en une fois? Seulement vous connaissez votre problème assez bien pour répondre.

Peut-être que vous pouvez lancer jvisualvm si vous avez JDK 6 et voir ce qui se passe avec la mémoire. Ce serait un excellent indice.

8
duffymo

Je ne suis pas sûr de l'efficacité de sa mémoire, mais ma première approche serait d'utiliser un Scanner car il est incroyablement facile à utiliser:

File file = new File("/path/to/my/file.txt");
Scanner input = new Scanner(file);

while(input.hasNext()) {
    String nextToken = input.next();
    //or to process line by line
    String nextLine = input.nextLine();
}

input.close();

Consultez l'API pour savoir comment modifier le délimiteur utilisé pour diviser les jetons.

11
Cogsy

Regardez ces pages. Ils contiennent de nombreux analyseurs CSV open source. JSaPar est l'un d'entre eux.

5
stenix

On dirait que vous avez actuellement 3 copies de tout le fichier en mémoire: le tableau d'octets, la chaîne et le tableau des lignes.

Au lieu de lire les octets dans un tableau d'octets, puis de les convertir en caractères à l'aide de new String(), il serait préférable d'utiliser un InputStreamReader, qui convertira les caractères de manière incrémentielle, plutôt que de commencer.

De plus, au lieu d’utiliser String.split ("\ n") pour obtenir les lignes individuelles, vous devez lire une ligne à la fois. Vous pouvez utiliser la méthode readLine() dans BufferedReader.

Essayez quelque chose comme ça:

BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream, "UTF-8"));
try {
  while (true) {
    String line = reader.readLine();
    if (line == null) break;
    String[] fields = line.split(",");
    // process fields here
  }
} finally {
  reader.close();
}
4

Si vous avez 200 000 000 fichiers de caractères et que vous les divisez tous les cinq caractères, vous avez 40 000 000 objets String. Supposons qu'ils partagent les données de caractères réelles avec les 400 Mo String d'origine (char est 2 octets). String est disons 32 octets, donc 1 280 000 000 octets d'objets String.

(Cela vaut probablement la peine de noter que cela dépend très de l'implémentation. split pourrait créer entièrement des chaînes avec un tout nouveau support char[] ou, OTOH, partager certaines valeurs String communes. Certaines implémentations Java interdisent l'utilisation du découpage de char[]. -comme une forme compacte et donner de très mauvais temps d’accès aléatoire.)

Même en supposant des chaînes plus longues, cela fait beaucoup d'objets. Avec autant de données, vous souhaiterez probablement travailler avec la plupart d'entre elles sous une forme compacte, comme l'original (uniquement avec des index). Convertissez uniquement en objets ce dont vous avez besoin. L'implémentation doit être semblable à une base de données (bien qu'ils ne gèrent habituellement pas efficacement les chaînes de longueur variable).

2

En appelant/appelant votre programme, vous pouvez utiliser cette commande: Java [-options] className [args ...]
à la place de [-options] fournit plus de mémoire, par exemple -Xmx1024m ou plus. mais ceci est juste une solution de contournement, vous devez changer votre mécanisme d'analyse syntaxique.

0
blackberry dev