50 votes

Le moyen le plus simple de supprimer les sauts de ligne en Perl

Je suis maintenant un script qui peut obtenir son entrée à partir de diverses sources, et travaille sur elle par ligne. En fonction de la source utilisée, mais les sauts de ligne peut être de type Unix, Windows, ou même, pour certains, agrégé d'entrée, mixte(!).

Lors de la lecture d'un fichier, il va quelque chose comme ceci:

@lines = <IN>;
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 avec quelque chose qui supprime soit de type Unix ou Windows de style mais les sauts de ligne. Je suis venue avec beaucoup trop de façons de résoudre cela, l'un de l'habitude inconvénients de Perl :)

Quel est votre avis sur la plus élégante façon de chomp hors générique mais les sauts de ligne? Ce qui serait le plus efficace?

Edit: Une petite précision - la méthode "process" permet d'obtenir une liste de lignes à partir de quelque part, de ne pas nessecarily lire à partir d'un fichier. Chaque ligne peut avoir

  • Pas de fuite mais les sauts de ligne
  • De style Unix, mais les sauts de ligne
  • De style Windows mais les sauts de ligne
  • Juste de Retour de Chariot (lorsque les données originales de style Windows mais les sauts de ligne, et il est lu avec $/ = '\n')
  • Un agrégées ensemble où les lignes ont des styles différents

88voto

Christoffer Points 6518

Après avoir fouillé un peu dans la documentation de perlre , je vais présenter ma meilleure suggestion jusqu'à présent, qui semble fonctionner assez bien. Perl 5.10 a ajouté la classe de caractères \ R sous la forme d'un saut de ligne généralisé:

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

C'est pareil que:

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

Je vais garder cette question ouverte pour le moment, juste pour voir s'il y a d'autres façons astucieuses d'attendre.

12voto

Ted Cambron Points 101

Chaque fois que je passe en saisie et que je veux supprimer ou remplacer des caractères, je le fais passer par de petits sous-programmes comme celui-ci.

 sub clean {

    my $text = shift;

    $text =~ s/\n//g;
    $text =~ s/\r//g;

    return $text;
}
 

Ce n'est peut-être pas compliqué, mais cette méthode fonctionne sans problème pour moi depuis des années.

7voto

Olfan Points 389

Lire un rapport, je suggérerais quelque chose comme

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

pour être sûr quelle que soit la plate-forme sur laquelle vous vous trouvez et le style de saut de ligne que vous êtes en train de traiter, car les éléments contenus dans \ r et \ n peuvent différer selon les saveurs Perl.

6voto

Kent Fredric Points 35592

l'extension de votre réponse

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

Fichier::Slurp résumés loin le Fichier IO trucs et retourne une chaîne de caractères pour vous.

NOTE

  1. Important de noter l'ajout d' /g , sans elle, donné un multi-ligne de chaîne, il suffira de remplacer le premier fautif de caractère.

  2. Aussi, la suppression de l' $, ce qui est redondant dans ce but, que nous voulons à la bande de tous les sauts de ligne, et pas seulement des sauts de lignes avant ce que l'on entend par $ sur cet OS.

  3. Dans un multi-chaîne de ligne, $ correspond à la fin de la chaîne et qui serait problématique ).

  4. Point 3 signifie que le point 2 est fait avec l'hypothèse que vous voulez également utiliser /m sinon, ' $ ' serait essentiellement de sens pour quelque chose de pratique dans une chaîne de caractères avec >1 lignes, ou, faire la seule ligne de traitement, un système d'exploitation qui, en fait, comprend $ et parvient à trouver l' \R* que procéder de la $

Exemples

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

Étant donné les notations ci-dessus, un système d'exploitation qui ne permet pas de comprendre quels que soient vos fichiers '\n' ou '\r' délimiteurs, dans le scénario par défaut avec le système d'exploitation par défaut de délimiteur défini pour $/ résultat sera à la lecture de votre fichier entier comme une seule chaîne de caractères ( à moins que votre chaîne a le $OS de délimiteurs, où elle permettra de délimiter par qui )

Dans ce cas, l'ensemble de 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?// : Lorsque efface uniquement la première 012\015 , \012 ou \015 séquence de, \015\012 entraîne \012 ou \015 émis.

  • /\R*$// : Si il arrive à être pas les séquences d'octets de "\015$OSDELIMITER' dans le fichier, alors PAS de mais les sauts de ligne seront supprimés, sauf pour les OS propres.

Il semblerait que personne ne comprend de quoi je parle, voici donc un exemple de code, qui est testé à ne PAS supprimer les sauts de ligne. L'exécutez, vous verrez qu'il laisse les sauts de ligne dans.

#!/usr/bin/perl 

use strict;
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 l' CLAIREMENT non Transformés de sortie, voir ici: http://pastebin.com/f2c063d74

Remarque il y a certaines combinaisons de travaux de cours, mais ils sont probablement ceux que vous avez vous-même naívely testé.

Notez que, dans cette sortie, tous les résultats doivent être de la forme >|$string|<>|$string|< avec PAS de retours à la LIGNE pour être considérée comme valide de sortie.

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

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

6voto

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X