# Découper une ligne d'un fichier en plusieurs lignes

**URL:** <https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965>\
**Category:** Programmation\
**Created:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965 "2016-02-21T13:56:37Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![fluo](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/f/e9bcb4/32.png) [@fluo](https://www.debian-fr.org/u/fluo)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/1 "2016-02-21T13:56:37Z")

</div>

Bonjour,

j’ai un fichier data.xml de 4 Go dont les données sont sur une seule ligne. Pour simplifier, voici le contenu de mon fichier xml :

```auto
<xml version="1.0" encoding="utf-8"?><dossier><fichier>abricot.txt</fichier><fichier>banane.txt</fichier><fichier>clémentine.txt</fichier></dossier></xml>
```

Si vous voulez voir plus clair :

```auto
<xml version="1.0" encoding="utf-8"?>
<dossier>
<fichier>abricot.txt</fichier>
<fichier>banane.txt</fichier>
<fichier>clémentine.txt</fichier>
</dossier>
</xml>
```

Je rappelle que les données sont sur une seule ligne dans le fichier data.xml. Comme le fichier est volumineux (4Go), je suis obligé de découper ce fichier en plusieurs fichiers pour pouvoir les parser sans saturer ma mémoire ram.  
Pour ce faire je recherche une commande permettant de découper le fichier data.xml lorsqu’il trouve à chaque fois la 1000 ème balise fermante . Ainsi on a 1.xml (qui contient les 1000 premières balise ), 2.xml (qui contient les 1000 balises suivantes), 3.xml (qui contient les 1000 balises suivantes), et ainsi de suite.

Une idée ?

C’est vrai que j’aurais pu utiliser sed -i “s/\>\</\>\n\</g” data.xml | split -d -l 1000, mais comme ma mémoire ram est limité, la partie sed de cette commande est aborté au bout d’un certain temps.

Merci d’avance, cordialement

---

<div class="post-metadata">

**Author:** ![MisterFreez](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/m/bb73d2/32.png) [@MisterFreez](https://www.debian-fr.org/u/MisterFreez)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/2 "2016-02-21T13:56:41Z")

</div>

> [@fluo](#):
>
> C’est vrai que j’aurais pu utiliser sed -i «&nbsp;s/\>\</\>\n\</g&nbsp;» data.xml | split -d -l 1000, mais comme ma mémoire ram est limité, la partie sed de cette commande est aborté au bout d’un certain temps.

Intéressant c’est ce que j’allais proposer.

Je pense que perl est ton ami.

[code]#!/usr/bin/env perl

use strict;  
use warnings;  
use 5.010;

my $filename = shift;

open FILE, ‹&nbsp;\<&nbsp;›, $filename or die «&nbsp;$!&nbsp;»;

my $size = 4096;  
my $limit = 1000;

my $buff1;  
my $buff2 = ‹&nbsp;&nbsp;›;  
my $index = 1;  
my $count = 0;

open OUT, ‹&nbsp;\>&nbsp;›, $index.‹&nbsp;.xml&nbsp;›;

while(read FILE, $buff1, $size) {  
chomp $buff1;  
$buff2 .= $buff1;  
while($buff2 =~ /(._?\</fichier\>)(._)/g) {  
++$count;  
if($count gt $limit) {  
++$index;  
close OUT;  
open OUT, ‹&nbsp;\>&nbsp;›, $index.‹&nbsp;.xml&nbsp;› or die «&nbsp;$!&nbsp;»;  
}  
say OUT $1;  
$buff2 = $2;  
}  
}  
say OUT $buff2;

close OUT;  
close FILE;[/code]  
Niveau perf ça doit pas être terrible parce qu’on utilise une lecture simple et pas un mmap, mais la consommation mémoire devrait rester correcte (voir très correcte). Faudrait aussi regarder si sed n’a pas une option pour refaire le comportement que j’ai écris en sed./

---

<div class="post-metadata">

**Author:** ![jb1](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/j/e36b37/32.png) [@jb1](https://www.debian-fr.org/u/jb1)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/3 "2016-02-21T13:56:42Z")

</div>

bonjour,  
pour la mémoire,  
le swap a quelle taille SVP?  
A+  
JB1

---

<div class="post-metadata">

**Author:** ![fluo](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/f/e9bcb4/32.png) [@fluo](https://www.debian-fr.org/u/fluo)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/4 "2016-02-21T13:56:43Z")

</div>

[quote=“jb1”]bonjour,  
pour la mémoire,  
le swap a quelle taille SVP?  
A+  
JB1[/quote]

```auto

root@localhost:/home/guest/# free
             total used free shared buffers cached
Mem: 3844084 1397792 2446292 0 33888 472988
-/+ buffers/cache: 890916 2953168
Swap: 8787960 29924 8758036
root@localhost:/home/guest/# free -m
             total used free shared buffers cached
Mem: 3753 1364 2389 0 33 460
-/+ buffers/cache: 870 2883
Swap: 8581 29 8552
root@localhost:/home/guest/# free -g
             total used free shared buffers cached
Mem: 3 1 2 0 0 0
-/+ buffers/cache: 0 2
Swap: 8 0 8
```

=\> J’ai 4 Go de Ram, et 8G pour la swap

---

<div class="post-metadata">

**Author:** ![fluo](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/f/e9bcb4/32.png) [@fluo](https://www.debian-fr.org/u/fluo)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/5 "2016-02-21T13:56:44Z")

</div>

Merci misterFreeze, j’ai essayé ton script, mais ça ne me coupe pas à toutes les 1000 , mais ça coupe à chaque , comme si le paramètre my $limit = 1000 a été zappé durant l’exécution du script.

Je n’ai jamais fait de perl dans ma vie (même si en voyant ton code, ça a l’air un language simpliste, mais de bonne réputation pour les regex).

---

<div class="post-metadata">

**Author:** ![MisterFreez](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/m/bb73d2/32.png) [@MisterFreez](https://www.debian-fr.org/u/MisterFreez)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/6 "2016-02-21T13:56:45Z")

</div>

Ok, je suis idiot (faut dire que j’ai écris mon script rapidement avant une réunion), il faut remettre à 0 le compteur :

[code]#!/usr/bin/env perl

use strict;  
use warnings;  
use 5.010;

my $filename = shift;

open FILE, ‘\<’, $filename or die “$!”;

my $size = 4096;  
my $limit = 1000;

my $buff1;  
my $buff2 = ‘’;  
my $index = 1;  
my $count = 0;

open OUT, ‘\>’, $index.’.xml’;

while(read FILE, $buff1, $size) {  
chomp $buff1;  
$buff2 .= $buff1;  
while($buff2 =~ /(._?\</fichier\>)(._)/g) {  
++$count;  
if($count gt $limit) {  
$count = 0;  
++$index;  
close OUT;  
open OUT, ‘\>’, $index.’.xml’ or die “$!”;  
}  
say OUT $1;  
$buff2 = $2;  
}  
}  
say OUT $buff2;

close OUT;  
close FILE;[/code]

Maintenant que j’ai pris le temps d’ouvrir le man de sed, tu devrais pouvoir avoir un résultat approchant avec :

```auto

```

Les noms de fichiers ne sont pas terribles mais ça doit être plus performant.

---

<div class="post-metadata">

**Author:** ![fluo](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/f/e9bcb4/32.png) [@fluo](https://www.debian-fr.org/u/fluo)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/7 "2016-02-21T13:56:45Z")

</div>

[quote=“MisterFreez”]Code:  
sed -l4096 ‘s@()@\1\n@g’ fichier.xml | split -dl1[/quote]  
=\> ça ne marche pas car ma mémoire ram n’est pas assez suffisante pour aller jusqu’au bout.  
Je laisse tomber sed, car j’ai l’impression que sed charge d’abord tout le fichier avant le parsage.

Pour le script perl corrigé, toujours le même résultat (pas de découpage à toutes les 1000 ), mais ce que j’aime bien dans ton script, il lit progressivement le fichier (comme le parsage événementiel d’un fichier xml).

---

<div class="post-metadata">

**Author:** ![jb1](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/j/e36b37/32.png) [@jb1](https://www.debian-fr.org/u/jb1)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/8 "2016-02-21T13:56:50Z")

</div>

bonjour,  
avec 8GB de swap cela devrait passer, il y a un pb a condition de travailler sous root (SHMMAX)  
et là cela swap!!!

une autre solution plus élègante et en rapport avec l’architecture,

on considère que le fichier d’une ligne passe dans un tuyau  
sur ce tuyau on ouvre une fenétre (taille buffer)  
on parcoure chaque caractère à la recherche de l’élèment indiquant une fin de phrase  
donc écrit la phrase, on rajoute \n dans ce fichier sortie  
résumons:  
-un fichier en entrée  
un fichier en sortie  
une fenètre de travail  
pour le fun on compte le nombre d’insertion (nombre de ligne)  
on inscrit également l’heure de début et de fin de traitement

pour les autres langages jocker!  
A+  
JB1  
 ![:079](https://www.debian-fr.org/uploads/default/original/1X/905412cc2e3f543d94c645aa4231b1f3197e2548.gif "flagelle")

mais prérequis il faut parler le C

---

<div class="post-metadata">

**Author:** ![MisterFreez](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/m/bb73d2/32.png) [@MisterFreez](https://www.debian-fr.org/u/MisterFreez)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/9 "2016-02-21T13:56:51Z")

</div>

[quote=«&nbsp;fluo&nbsp;»][quote=«&nbsp;MisterFreez&nbsp;»]Code:  
sed -l4096 ‹&nbsp;s@()@\1\n@g&nbsp;› fichier.xml | split -dl1[/quote]  
=\> ça ne marche pas car ma mémoire ram n’est pas assez suffisante pour aller jusqu’au bout.  
Je laisse tomber sed, car j’ai l’impression que sed charge d’abord tout le fichier avant le parsage.[/quote]  
Ça me déçoit de sa part…

> [@fluo](#):
>
> Pour le script perl corrigé, toujours le même résultat (pas de découpage à toutes les 1000 ), mais ce que j’aime bien dans ton script, il lit progressivement le fichier (comme le parsage événementiel d’un fichier xml).

Hum, je suis perplexe. Voici un version qui me donnera peut être plus d’infos :

[code]#!/usr/bin/env perl

use strict;  
use warnings;  
use 5.010;

my $filename = shift;

open FILE, ‹&nbsp;\<&nbsp;›, $filename or die «&nbsp;$!&nbsp;»;

my $size = 4096;  
my $limit = 1000;

my $buff1;  
my $buff2 = ‹&nbsp;&nbsp;›;  
my $index = 1;  
my $count = 0;

open OUT, ‹&nbsp;\>&nbsp;›, $index.‹&nbsp;.xml&nbsp;›;

while(read FILE, $buff1, $size) {  
chomp $buff1;  
$buff2 .= $buff1;  
while($buff2 =~ /(._?\</fichier\>)(._)/g) {  
++$count;  
if($count gt $limit) {  
die «&nbsp;count = $count ; limit = $limit ; index = $index&nbsp;»;  
$count = 0;  
++$index;  
close OUT;  
open OUT, ‹&nbsp;\>&nbsp;›, $index.‹&nbsp;.xml&nbsp;› or die «&nbsp;$!&nbsp;»;  
}  
say OUT $1;  
$buff2 = $2;  
}  
}  
say OUT $buff2;

close OUT;  
close FILE;[/code]

[quote=«&nbsp;jb1&nbsp;»]on considère que le fichier d’une ligne passe dans un tuyau  
sur ce tuyau on ouvre une fenétre (taille buffer)  
on parcoure chaque caractère à la recherche de l’élément indiquant une fin de phrase  
donc écrit la phrase, on rajoute \n dans ce fichier sortie[/quote]  
Tu vois une grande différence avec ce que fais mon script perl ?  
[ul]  
[li]Lire le fichier petit à petit dans un buffer[/li]  
[li]découper selon le motif et écrire dans un fichier[/li]  
[li]changer de fichier dès qu’on passe une certaine limite[/li][/ul]

> [@jb1](#):
>
> mais prérequis il faut parler le C

Pourquoi ça ? Éventuellement si tu cherche à utiliser des pipes ou mmap ça peut se comprendre mais c’est dispo dans les autres langages aussi.

---

<div class="post-metadata">

**Author:** ![fluo](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/f/e9bcb4/32.png) [@fluo](https://www.debian-fr.org/u/fluo)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/10 "2016-02-21T13:56:53Z")

</div>

Merci misterFreeze, ça marche, ça consomme très peu de mémoire.  
Mais d’après toi, qui est le plus rapide (je voudrais un classement du plus rapide vers le moins rapide) pour le parsage de gros fichier avec regex en ligne de commande ? : bash, php, perl, python ?

---

<div class="post-metadata">

**Author:** ![MisterFreez](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/m/bb73d2/32.png) [@MisterFreez](https://www.debian-fr.org/u/MisterFreez)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/11 "2016-02-21T13:56:53Z")

</div>

> [@fluo](#):
>
> Mais d’après toi, qui est le plus rapide (je voudrais un classement du plus rapide vers le moins rapide) pour le parsage de gros fichier avec regex en ligne de commande ? : bash, php, perl, python ?

Probablement perl, les autres sont tous au même niveau quant au traitement des expressions régulières, ils utilisent PCRE qui est écrite en C.  
La performance intrinsèque des langage (et leur gestion des chaines de caractère par exemple) jour probablement un rôle pus déterminant. perl a simplement l’avantage d’avoir des expressions régulières extrêmement poussées (cf. : [articles.mongueurs.net/magazines … ag106.html](http://articles.mongueurs.net/magazines/linuxmag106.html)). Une expression régulière ça s’optimise.

En l’occurrence dans le cas présent je présume que le traitement a du être très long. C’est la lecture du fichier qui cause principalement ce problème. J’ai choisi d’avoir un buffer de 4096 caractères en ayant dans l’idée que c’était de l’ASCII et ainsi lire toujours l’équivalent d’un buffer noyau (une page mémoire soit 4096o), mais c’est loin d’être optimal. Ce qu’il faudrait c’est utiliser Mmap et utiliser une taille de buffer qui correspond à la taille moyenne du contenu de la balise comme ça :  
[ul]  
[li]tu laisse le noyau faire ça tambouille pour mapper le fichier en mémoire de manière optimisé[/li]  
[li]tu limite le nombre de tour de boucle qui contient l’expression régulière[/li][/ul]

Note : je suis pas un expert des expressions régulières, mais je pense qu’il est possible d’optimiser celle que j’ai donné pour supprimer la boucle. Ce serais probablement plus propre et peut être plus rapide.

---

<div class="post-metadata">

**Author:** ![fluo](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/f/e9bcb4/32.png) [@fluo](https://www.debian-fr.org/u/fluo)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/12 "2016-02-21T13:56:54Z")

</div>

Merci pour ces explications 🙂 .

---

<div class="post-metadata">

**Author:** ![jb1](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/j/e36b37/32.png) [@jb1](https://www.debian-fr.org/u/jb1)\
**Post date:** [Février 21, 2016, 1:56 UTC](https://www.debian-fr.org/t/decouper-une-ligne-dun-fichier-en-plusieurs-lignes/55965/13 "2016-02-21T13:56:56Z")

</div>

bonsoir MisterFreez et les autres,  
mon problème, je ne parle pas «&nbsp;Perl&nbsp;» mais d’autres langages  
bref:  
pour la mémoire si 8GB de barrettes la valeur de shmmax est à revoir  
A+  
JB1  
 ![8)](https://www.debian-fr.org/uploads/default/original/1X/ae031e9834d19166598edd060b063d44f1829139.gif "Cool")
