Question

Je maintiens un script qui peut obtenir ses contributions à partir de diverses sources et y travaille par ligne. Selon la source réelle utilisée, les brise-ligne peuvent être de style Unix, de style Windows ou même, pour une entrée agrégée, mixte (!).

Lors de la lecture d'un fichier, il s'agit de 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, c'est remplacer le chomp par quelque chose qui supprime soit des brise-ligne de style Unix ou de style Windows. Je trouve beaucoup trop de façons de résoudre ce problème, l'un des inconvénients habituels de Perl :)

Quelle est votre opinion sur la façon la plus soignée de chomper des brise-ligne génériques? Quel serait le plus efficace?

Edit: une petite clarification - La méthode «processus» obtient une liste de lignes de quelque part, pas non plus lu à partir d'un fichier. Chaque ligne pourrait avoir

  • Pas de brise-ligne de fuite
  • Breaks de style Unix
  • Breaks de style Windows
  • Juste le retour en voiture (lorsque les données d'origine ont des brise-ligne de style Windows et sont lues avec $ / = ' n')
  • Un ensemble agrégé où les lignes ont des styles différents
Était-ce utile?

La solution

Après avoir creusé un peu à travers le perlre Docs un peu, je présenterai ma meilleure suggestion jusqu'à présent qui semble fonctionner assez bien. Perl 5.10 a ajouté la classe de caractères r comme un panneau 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 garderai cette question ouverte un moment encore, juste pour voir s'il y a plus de façons astucieuses en attendant d'être suggérées.

Autres conseils

Chaque fois que je passe par l'entrée et que je veux supprimer ou remplacer les caractères, je l'exécute à travers 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 sophistiqué, mais cette méthode fonctionne sans faille pour moi depuis des années.

En lisant perlport Je suggérerais quelque chose comme

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

Pour être sans danger pour la plate-forme sur laquelle vous vous trouvez et quel que soit le style de flux de ligne que vous pourriez traiter, car ce qui est dans r et n peut différer par différentes saveurs de perl.

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

Remarque de 2017: Fichier :: Slurp n'est pas recommandé en raison d'erreurs de conception et d'erreurs non tenues. Utilisation Fichier :: slurper ou Chemin :: Tiny Au lieu.

s'étendre sur votre réponse

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

Fichier :: Slurp résume le fichier IO Stuff et renvoie simplement une chaîne pour vous.

REMARQUE

  1. Important de noter l'ajout de /g , sans lui, étant donné une chaîne multi-lignes, elle ne remplacera que le première caractère offensant.

  2. De plus, la suppression de $, qui est redondant à cet effet, comme nous voulons se déshabiller tout les ruptures de ligne, pas seulement les brise-ligne avant tout ce qui signifie par $ sur ce système d'exploitation.

  3. Dans une chaîne multi-lignes, $ correspond à la fin du chaîne de caractères et ce serait problématique).

  4. Le point 3 signifie que le point 2 est fait avec l'hypothèse que vous voudriez également utiliser /m Sinon, «$» serait fondamentalement dénué de sens pour tout ce qui est pratique dans une chaîne avec> 1 lignes, ou, faisant un traitement à ligne unique, un système d'exploitation qui comprend réellement $ et parvient à trouver le \R* qui continuent le $

Exemples

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

Compte tenu de la notation ci-dessus, un système d'exploitation qui ne comprend pas quel que soit vos fichiers ' n' ou ' r' délimiter, dans le scénario par défaut avec le délimiteur par défaut du système $/ 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ù il délimitera par cela)

Donc, dans ce cas, tous ces examens sont inutiles:

  • /\R*$// : Effacera seulement la dernière séquence de \R dans le fichier
  • /\R*// : Effacera seulement la première séquence de \R dans le fichier
  • /\012?\015?// : Quand effacera seulement le premier 012\015 , \012 , ou \015 séquence, \015\012 entraînera soit \012 ou \015 être émis.

  • /\R*$// : S'il n'y a pas de séquences d'octets de ' 015 $ osdelimiter' dans le fichier, alors alors NON Les brise-ligne seront supprimés à l'exception des propres OS.

Il semblerait que personne ne comprenne ce dont je parle, alors voici un exemple de code, c'est-à-dire testé à NE PAS Retirez les flux de ligne. Exécutez-le, vous verrez qu'il laisse les alignements de ligne.

#!/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 le CLAIREMENT Sortie non transformée, voir ici: http://pastebin.com/f2c063d74

Notez qu'il existe certaines combinaisons qui fonctionnent bien sûr, mais ce sont probablement ceux que vous avez vous-même testés.

Notez que dans cette sortie, tous les résultats doivent être du formulaire >|$string|<>|$string|< avec Pas de flux de ligne être considéré comme une sortie valide.

et $string 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 y aller:

chomp(@lines);

Ou:

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

Ou:

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

Utilisation de ceux-ci directement sur un fichier:

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

perl -e 'chomp(@a=<>);print @a' <a.txt |less

Pour étendre la réponse de Ted Cambron ci-dessus et quelque chose qui n'a pas été traité ici: si vous supprimez toutes les ruptures de ligne sans discrimination d'un morceau de texte entré, vous vous retrouverez avec des paragraphes qui se heurtent les uns aux autres sans espaces lorsque vous publierez 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 'gourmand' afin qu'il continue de trouver des doubles espaces jusqu'à ce qu'il les remplace tous. (Substituant efficacement quelque chose de plus cet espace unique)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top