سؤال

أحافظ على برنامج نصي يمكنه الحصول على مدخلاته من مصادر مختلفة ، وأعمل عليه في كل سطر. اعتمادًا على المصدر الفعلي المستخدم ، قد تكون خطوط الخطوط على غرار Unix أو على غرار Windows أو حتى بالنسبة لبعض المدخلات المجمعة ، مختلطة (!).

عند القراءة من ملف يذهب شيء من هذا القبيل:

@lines = <IN>;
process(\@lines);

...

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

لذلك ، ما أحتاج إلى القيام به هو استبدال chomp بشيء يزيل إما قصاصات الخطوط على طراز Unix أو Windows. أنا أتوصل إلى العديد من الطرق لحل هذا ، واحدة من عيوب بيرل المعتادة :)

ما رأيك في أفضل طريقة لتخليص خطوط الخطوط العامة؟ ماذا سيكون الأكثر كفاءة؟

تحرير: توضيح صغير - تحصل طريقة "عملية" على قائمة من الخطوط من مكان ما ، لا تقرأ بشكل نيس من ملف. قد يكون لكل سطر

  • لا خطوط خطية
  • يونيكس على غرار الخطوط
  • breaks على طراز Windows
  • فقط عائد النقل (عندما يكون للبيانات الأصلية قصاصات على غرار Windows ويتم قراءتها باستخدام $/ = ' n')
  • مجموعة مجمعة حيث يكون للخطوط أنماط مختلفة
هل كانت مفيدة؟

المحلول

بعد الحفر قليلاً من خلال بيرلري مستندات قليلاً ، سأقدم أفضل اقتراحي حتى الآن يبدو أنه يعمل بشكل جيد. أضاف Perl 5.10 فئة الأحرف r كخط خط معمم:

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

إنه نفس:

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

سأبقي هذا السؤال مفتوحًا لفترة من الوقت ، فقط لمعرفة ما إذا كانت هناك طرق أكثر أناقة في انتظار اقتراحها.

نصائح أخرى

كلما ذهبت من خلال الإدخال وأرغب في إزالة أو استبدال الأحرف التي أقوم بتشغيلها من خلال روتين فرعي صغير مثل هذا.

sub clean {

    my $text = shift;

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

    return $text;
}

قد لا يكون هذا خيالًا ، لكن هذه الطريقة كانت تعمل لا تشوبها شائبة بالنسبة لي لسنوات.

قراءة perlport أود أن أقترح شيئًا مثل

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

لكي تكون آمنًا لأي منصة تقوم بها وأي نمط من خطوط خط قد تقوم بمعالجته لأن ما في r و n قد يختلف من خلال نكهات Perl المختلفة.

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

ملاحظة من 2017: ملف :: لا ينصح به SLURP بسبب أخطاء التصميم والأخطاء غير المحافظة. يستخدم ملف :: slurper أو المسار :: صغير في حين أن.

تمتد على إجابتك

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

File :: Slurp Abstracts بعيدًا عن ملفات ملف io وأرجع سلسلة لك فقط.

ملاحظة

  1. من المهم ملاحظة إضافة /g ، بدونها ، بالنظر إلى سلسلة متعددة الخطوط ، سوف تحل محل فقط أول شخصية مخالفة.

  2. أيضا ، إزالة $, ، وهو أمر زائد لهذا الغرض ، كما نريد التجريد الكل كسر الخط ، وليس فقط خرق الخط قبل كل ما هو المقصود به $ على هذا نظام التشغيل.

  3. في سلسلة متعددة ، $ يطابق نهاية سلسلة وهذا سيكون مشكلة).

  4. النقطة 3 تعني أن النقطة 2 تتم مع افتراض أنك تريد أيضًا استخدامه /m وإلا فإن "$" سيكون بلا معنى بشكل أساسي لأي شيء عملي في سلسلة مع> 1 خطوط ، أو ، معالجة خط واحد ، نظام التشغيل الذي يفهم فعليًا $ وتمكن من العثور على \R* التي تستمر $

أمثلة

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

بالنظر إلى التدوين أعلاه ، نظام التشغيل الذي لا يفهم أي ملفات " n" أو " r" ، في السيناريو الافتراضي مع تعيين محدد نظام التشغيل $/ سيؤدي إلى قراءة ملفك بالكامل كسلسلة متجاورة واحدة (ما لم يكن لسلسلة محددات OS $ فيه ، حيث ستحدد بذلك)

لذلك في هذه الحالة ، كل هذه regex عديمة الفائدة:

  • /\R*$// : سوف يمحو التسلسل الأخير فقط \R في الملف
  • /\R*// : سوف يمحو التسلسل الأول فقط من \R في الملف
  • /\012?\015?// : متى سوف يمحو الأول فقط 012\015 , \012 ، أو \015 تسلسل، \015\012 سوف يؤدي إلى أي منهما \012 أو \015 يجري الانبعاث.

  • /\R*$// : إذا كان هناك عدم وجود تسلسل بايت من " 015 $ osdelimiter" في الملف ، ثم رقم ستتم إزالة خطوط الخطوط المتوفرة باستثناء تلك الخاصة بنظام التشغيل.

لا يبدو أن لا أحد يحصل على ما أتحدث عنه ، لذا فإليك رمز المثال ، أي تم اختباره إلى ليس إزالة خلاصات الخط. قم بتشغيله ، سترى أنه يترك خطوط الخطوط.

#!/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 };
    }
}

ل بوضوح ناتج غير معالج ، انظر هنا: http://pastebin.com/f2c063d74

لاحظ أن هناك مجموعات معينة تعمل بالطبع ، لكنها على الأرجح تلك التي اختبرتها بنفسك.

لاحظ أنه في هذا الإخراج ، يجب أن تكون جميع النتائج من النموذج >|$string|<>|$string|< مع لا يوجد خط تغذية لاعتبار ناتج صالح.

و $string من الشكل العام {0}$data{1}$delimiter{2} حيث في جميع مصادر الإخراج ، يجب أن يكون هناك:

  1. لا شيء بين {1} و {2}
  2. فقط |<>| ما بين {1} و {2}

في مثالك ، يمكنك فقط الذهاب:

chomp(@lines);

أو:

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

أو:

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

باستخدام هذه مباشرة على ملف:

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

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

لتمديد إجابة تيد كامبرون أعلاه وشيء لم يتم معالجته هنا: إذا قمت بإزالة جميع فواصل الأسطر بشكل عشوائي من جزء من النص الذي تم إدخاله ، فسوف ينتهي بك الأمر بالفقرات التي تسير في بعضها البعض دون إخراج هذا النص لاحقًا. هذا ما أستخدمه:

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;
}

يستخدم الاستبدال الأخير المعدل G "الجشع" بحيث يستمر في العثور على مساحات مزدوجة حتى يحل محلها جميعًا. (استبدال أي شيء واحد بفعالية مساحة واحدة)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top