Le meilleur moyen de supprimer les sauts de ligne en Perl

Je maintiens un script qui peut recevoir ses entrées de diverses sources et y travaille par ligne. Selon la source réelle utilisée, les sauts de ligne peuvent être de type Unix, de style Windows ou même, pour certaines entrées agrégées, mixés (!).

En lisant un fichier, cela ressemble à ceci:

@lines = ; process(\@lines); ... sub process { @lines = shift; foreach my $line (@{$lines}) { chomp $line; #Handle line by line } } 

Donc, ce que je dois faire est de remplacer le chomp par quelque chose qui supprime les sauts de ligne de style Unix ou Windows. J’arrive avec trop de façons de résoudre ce problème, l’un des inconvénients habituels de Perl 🙂

Quelle est votre opinion sur le moyen le plus efficace de compenser les sauts de ligne génériques? Quel serait le plus efficace?

Edit: Une petite précision – la méthode ‘process’ obtient une liste de lignes à partir de quelque part, pas nécessairement lue dans un fichier . Chaque ligne peut avoir

  • Pas de saut de ligne
  • Les sauts de ligne de style Unix
  • Coups de lignes de style Windows
  • Just Carriage-Return (lorsque les données d’origine ont des sauts de ligne de style Windows et sont lues avec $ / = ‘\ n’)
  • Un ensemble agrégé où les lignes ont des styles différents

Après avoir creusé un peu la documentation, je vais vous présenter ma meilleure suggestion jusqu’à présent qui semble bien fonctionner. Perl 5.10 a ajouté la classe de caractères \ R comme saut de ligne généralisé:

 $line =~ s/\R//g; 

C’est la même chose que:

 (?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}]) 

Je vais garder cette question ouverte encore un peu, juste pour voir s’il y a plus de manières astucieuses à attendre.

Chaque fois que je passe par input et que je veux supprimer ou remplacer des caractères, je le lance à travers de petites sous-routines comme celle-ci.

 sub clean { my $text = shift; $text =~ s/\n//g; $text =~ s/\r//g; return $text; } 

Ce n’est peut-être pas très sophistiqué, mais cette méthode fonctionne sans problème depuis des années.

Lire perlport Je suggère quelque chose comme

 $line =~ s/\015?\012?$//; 

pour être sûr quelle que soit la plate-forme sur laquelle vous êtes et quel que soit le style de saut de ligne que vous êtes en train de traiter, car ce qui est dans \ r et \ n peut différer selon les différentes saveurs de Perl.

 $line =~ s/[\r\n]+//g; 

Note de 2017: File :: Slurp n’est pas recommandé en raison d’erreurs de conception et d’erreurs non maintenues. Utilisez plutôt File :: Slurper ou Path :: Tiny à la place.

prolongeant votre réponse

 use File::Slurp (); my $value = File::Slurp::slurp($filename); $value =~ s/\R*//g; 

File :: Slurp élimine les fichiers d’E / S et renvoie simplement une chaîne pour vous.

REMARQUE

  1. Important de noter l’addition de /g , sans elle, étant donné une chaîne multi-lignes, elle ne remplacera que le premier caractère incriminé.

  2. En outre, la suppression de $ , qui est redondante à cet effet, car nous voulons supprimer tous les sauts de ligne, et pas seulement les sauts de ligne avant tout ce que l’on entend par $ sur ce système d’exploitation.

  3. Dans une chaîne multi-lignes, $ correspond à la fin de la chaîne et cela poserait problème.

  4. Le point 3 signifie que le point 2 est fait avec l’hypothèse que vous voudriez aussi utiliser /m sinon “$” serait sans signification pour tout ce qui est pratique dans une chaîne avec> 1 ligne, ou, en traitement de ligne unique, un système d’exploitation comprend effectivement $ et parvient à trouver le \R* qui procède le $

Exemples

 while( my $line = <$foo> ){ $line =~ $regex; } 

Compte tenu de la notation ci-dessus, un système d’exploitation qui ne comprend pas vos délimiteurs de fichiers \ n ‘ou \ r’, dans le scénario par défaut avec le délimiteur par défaut du système d’exploitation défini pour $/ entraînera la lecture de votre fichier entier comme une chaîne contiguë ( sauf si votre chaîne contient les délimiteurs de $ OS, où elle sera délimitée par cela)

Donc, dans ce cas, toutes ces regex sont inutiles:

  • /\R*$// : efface uniquement la dernière séquence de \R dans le fichier
  • /\R*// : efface uniquement la première séquence de \R dans le fichier
  • /\012?\015?// : Quand efface uniquement la première séquence 012\015 , \012 ou \015 , \015\012 entraîne soit l’émission de \012 ou \015 .

  • /\R*$// : S’il n’y a pas de séquence d’octets de ‘\ 015 $ OSDELIMITER’ dans le fichier, alors aucun saut de ligne ne sera supprimé, à l’exception de ceux du système d’exploitation.

Il semblerait que personne n’obtienne ce dont je parle, donc voici un exemple de code, testé pour ne PAS supprimer les sauts de ligne. Lancez-le, vous verrez qu’il laisse les sauts de ligne.

 #!/usr/bin/perl use ssortingct; use warnings; my $fn = 'TestFile.txt'; my $LF = "\012"; my $CR = "\015"; my $UnixNL = $LF; my $DOSNL = $CR . $LF; my $MacNL = $CR; sub generate { my $filename = shift; my $lineDelimiter = shift; open my $fh, '>', $filename; for ( 0 .. 10 ) { print $fh "{0}"; print $fh join "", map { chr( int( rand(26) + 60 ) ) } 0 .. 20; print $fh "{1}"; print $fh $lineDelimiter->(); print $fh "{2}"; } close $fh; } sub parse { my $filename = shift; my $osDelimiter = shift; my $message = shift; print "Parsing $message File $filename : \n"; local $/ = $osDelimiter; open my $fh, '<', $filename; while ( my $line = <$fh> ) { $line =~ s/\R*$//; print ">|" . $line . "|<"; } print "Done.\n\n"; } my @all = ( $DOSNL,$MacNL,$UnixNL); generate 'Windows.txt' , sub { $DOSNL }; generate 'Mac.txt' , sub { $MacNL }; generate 'Unix.txt', sub { $UnixNL }; generate 'Mixed.txt', sub { return @all[ int(rand(2)) ]; }; for my $os ( ["$MacNL", "On Mac"], ["$DOSNL", "On Windows"], ["$UnixNL", "On Unix"]){ for ( qw( Windows Mac Unix Mixed ) ){ parse $_ . ".txt", @{ $os }; } } 

Pour la sortie CLEARLY Unprocessed, voir ici: http://pastebin.com/f2c063d74

Notez que certaines combinaisons fonctionnent bien sûr, mais elles sont probablement celles que vous avez vous-même testées.

Notez que dans cette sortie, tous les résultats doivent être de la forme >|$ssortingng|<>|$string|< avec NO LINE FEEDS à considérer comme sortie valide.

et $ssortingng est de la forme générale {0}$data{1}$delimiter{2} où dans toutes les sources de sortie, il devrait y avoir soit:

  1. Rien entre {1} et {2}
  2. seulement |<>| entre {1} et {2}

Dans votre exemple, vous pouvez simplement aller:

 chomp(@lines); 

Ou:

 $_=join("", @lines); s/[\r\n]+//g; 

Ou:

 @lines = split /[\r\n]+/, join("", @lines); 

En les utilisant directement sur un fichier:

 perl -e '$_=join("",<>); s/[\r\n]+//g; print' );print @a'  

Pour étendre la réponse de Ted Cambron ci-dessus et quelque chose qui n’a pas été traité ici: Si vous supprimez tous les sauts de ligne sans distinction d’un morceau de texte saisi, vous vous retrouverez avec des paragraphes sans espaces lorsque vous produirez ce texte plus tard. C’est ce que j’utilise:

 sub cleanLines{ my $text = shift; $text =~ s/\r/ /; #replace \r with space $text =~ s/\n/ /; #replace \n with space $text =~ s/ / /g; #replace double-spaces with single space return $text; } 

La dernière substitution utilise le modificateur g ‘greedy’ et continue donc à trouver des espaces doubles jusqu’à ce qu’ils les remplacent tous. (En remplaçant efficacement tout autre espace unique)