Numeracja linii - zaimplementuj nl


13

Twoim zadaniem jest wdrożenie programu podobnego do nlnarzędzia wiersza poleceń z podstawowych narzędzi GNU.

Standardowe luki są zabronione.

Nie możesz używać żadnej wbudowanej lub zewnętrznej funkcji, programu lub narzędzia do numerowania linii pliku lub łańcucha, takich jak on nlsam lub =polecenie w GNU sed.

Specyfikacja

Wejście

Program akceptuje nazwy plików jako argumenty. Twój kod nie musi być wieloplatformowy; należy zastosować format nazwy pliku systemu operacyjnego z uruchomionym kodem, tzn. jeśli zdarzyło Ci się być w systemie Windows, separatorem katalogów może być \lub /.

Musisz mieć możliwość pobrania 64 plików wejściowych, w tym -jeśli jest to określone. Jeśli podano więcej niż 64, obsłuż tylko pierwsze 64.

Na liście nazw plików -reprezentuje standardowe dane wejściowe.

Jeśli podano nazwy plików, czytaj z plików w kolejności, w jakiej zostały podane, i konkatenuj ich zawartość, wstawiając nowy wiersz między nimi i na końcu. Jeśli nie możesz odczytać z jednego lub więcej nazw plików (ponieważ plik nie istnieje lub nie masz do niego uprawnień do odczytu), zignoruj ​​je. Jeśli wszystkie podane nazwy plików są niepoprawne, nie wysyłaj nic.

Jeśli nie podano nazw plików, czytaj ze standardowego wejścia. Czytaj ze standardowego wejścia tylko wtedy, gdy nie podano nazw plików lub jeśli -podano.

Wynik

Wyjście programu wola, na standardowe wyjście, wejście z liniami numerowane w ten sposób (Można zakładać, że wejście ma \n, \r\nlub \rkońca linii; odebrać w zależności co jest wygodne dla Ciebie, ale określić, który z nich):

<5 spaces>1<tab><content of line 1 of input>
<5 spaces>2<tab><content of line 2 of input>
...
<4 spaces>10<tab><content of line 10 of input>
...
<3 spaces>100<tab><content of line 100 of input>
...
...

6 znaków spacji jest przypisanych do numeru linii i jest on wstawiany na końcu tych znaków; reszta staje się polami (np. 1będzie miała 5 pól wiodących, 22będzie miała 4 pola wiodące, ...). Jeśli dane wejściowe są wystarczająco długie, w końcu zabraknie miejsca na numer linii w linii 999999. Nie możesz niczego wypisywać po wierszu 999999.

Jeśli wejście jest puste, nie wysyłaj nic.

Wyjdź ze statusu

Niższe liczby mają pierwszeństwo: jeśli napotkano błędy 1 i 2, należy wyjść ze statusem 1.

Wyjdź ze statusem 0, jeśli wejście zostało pomyślnie odebrane, a linie pomyślnie ponumerowane i wyprowadzone.

Wyjdź ze statusem 1, jeśli co najmniej jeden z plików określonych w wierszu poleceń nie został znaleziony lub nie można go odczytać.

Wyjdź ze statusem 2, jeśli podano zbyt wiele plików (więcej niż 64).

Wyjdź ze statusem 3, jeśli dane wejściowe były zbyt długie (ponad 999999 linii). \

Punktacja

To jest golf golfowy - wygrywa najkrótszy program!

Mogę dodać bonusy później za wdrożenie niektórych opcji, które nlma. W tej chwili nie ma żadnych bonusów.


Czy numeracja linii jest ciągła czy krótka, że ​​„resetuje się” sama dla każdego pliku?
britishtea

@britishtea Jest ciągły

1
Czy konieczne jest użycie węzła, jeśli chcemy przesłać coś w js? Czy możemy użyć funkcji args lub prompt()emulować argumenty programu i standardowe wyjście?
DankMemes,

1
Pliki binarne? Kodowanie? Markery Unicode?
edc65

Odpowiedzi:


6

Bash, 121

s=$[2*($#>64)]
for f in "$@";{ [ -f $f ]||s=1;}
((s))&&exit $s
awk '{if(NR>=10**6){exit 3}printf("%6d\t%s\n",NR,$0)}' $@

1
Możesz sprawić, że ifwyrażenia będą nieco krótsze, jeśli użyjesz wyrażeń arytmetycznych, np.(($#>64))&&s=2
Digital Trauma

2
@DigitalTrauma Nauczyłem się czegoś!
Sammitch

1
Można wymienić s=0;(($#>64))&&s=2z s=$[2*($#>64)], (($s==0))||z ((s))&&, a ifoświadczenie z [ -f "$f" ]||s=1.
Dennis


2
awkpołączy także wiele plików, więc oficjalnie liczy się to jako bezużyteczne użycie cat ;-). Zamiast tego myślę, że to zadziała:awk '...' $@
Cyfrowa trauma

2

Ruby, 195

o,l=$*[64]?[2]:[],999999
($*==[]?[?-]:$*).each{|n|f=n==?-?STDIN: open(n)rescue o<<1&&next
s=f.read.lines
s[l]?o<<3:1
puts s[0..l].map.with_index(1){|l,i|i.to_s.rjust(6)+?\t+l}}
exit !o[0]?0:o.min

Myślę, że STDINjest alias do $<.
Martin Ender

Jest to alias dla ARGF, który odczyta również z pozostałych plików podanych jako argumenty. Myślę, że można to ARGFjakoś pograć w golfa (wydaje się nawet rozpoznawać "-"jako standardowe).
britishtea

britishteanl: 4: in block in <main>': undefined method [] 'for # <Enumerator: 0x000006002980c8> (NoMethodError) from britishteanl: 2: in each' from britishteanl:2:in <main>' - co jest nie tak? Uruchomiłem jakoruby britishteanl folder/filename

Podejrzewam, że to różnica w wersji Ruby. Próbkę uruchomiłem na Ruby 2.0.0 i Ruby 2.1.2 bez problemów. Jakiej wersji używasz?
britishtea

2

Perl, 84 + 2 ( -pl) = 86 bajtów

perl -ple'BEGIN{map{-r||exit 1}@ARGV;@ARGV>63&&exit 2}$.>=1e6&&exit 3;$_=printf"%5d\t%s",$.,$_'

Wydzielono:

perl -MO=Deparse -ple'BEGIN{map{-r||exit 1}@ARGV;@ARGV>63&&exit 2}$.>=1e6&&exit 3;$_=printf"%5d\t%s",$.,$_' output.txt; echo $?

BEGIN { $/ = "\n"; $\ = "\n"; }
sub BEGIN {
    map {exit 1 unless -r $_;} @ARGV;
    exit 2 if @ARGV > 63;
}
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    exit 3 if $. >= 1000000;
    $_ = printf("%5d\t%s", $., $_);
}
continue {
    die "-p destination: $!\n" unless print $_;
}
-e syntax OK

Ważne, aby wiedzieć:

  • -potacza program podany -ew pętli while/continue
  • BEGIN kod zostanie wykonany przed (domyślną) częścią główną
  • test pliku -rrównież kończy się niepowodzeniem, jeśli plik nie istnieje !-ei domyślnie jest testowany $_, domyślnie podany wmap { ... } @ARGV
  • $. przechowuje bieżący numer linii
  • reszta powinna być zrozumiała;)

Świetna odpowiedź i witamy w Programowaniu Puzzle i Code Golf! Być może możesz edytować, aby dodać objaśnienie działania kodu.
wizzwizz4,

1

python 173

import os,sys
c=0
l=1
for f in sys.argv[1:]:
    if c>64:exit(2)
    if not os.path.isfile(f):exit(1)
    c+=1
    for x in open(f):
        if l>=10**6:exit(3)
        print '%6d\t%s'%(l,x),;l+=1

Myślę, że w twoim kodzie brakuje obecnie -dla sys.stdin. Możliwym rozwiązaniem może być coś takiego, fh=sys.stdin if f=='-' else open(f)a potem iść x=fh.readline()? Niestety nie czyni go to jednak krótszym. :)
Dave J

1

J (162)

exit(((2*64<#)[exit@3:`(stdout@(,&LF)@;@(,&TAB@(6&":)&.>@>:@i.@#,&.>]))@.(1e6>#)@(<;.2)@(1!:1)@(<`3:@.('-'-:]))&.>@;@{.@(_64&(<\))) ::1:)]`(]&<&'-')@.(0=#)2}.ARGV

Wyjaśnienie:

  • ]`(]&<&'-')@.(0=#)2}.ARGV: Pobierz argumenty wiersza poleceń i usuń dwa pierwsze (ponieważ są to interpreter i nazwa pliku skryptu). Jeśli wynikowa lista jest pusta, zwróć ['-'](tzn. Jakby użytkownik tylko zdał -), w przeciwnym razie zwróć listę bez zmian.
  • (... ::1:): jeśli funkcja wewnętrzna zawiedzie, wróć 1, w przeciwnym razie zwróć wszystko, co funkcja wewnętrzna zwróciła.
  • ((2*64<#)[... ): oceń funkcję wewnętrzną i wyrzuć wynik. Następnie, jeśli długość przekazanej listy nie była większa niż 64, zwróć 0, w przeciwnym razie zwróć 2.
  • &.>@;@{.@(_64&(<\)): pobierz co najwyżej 64elementy z listy i dla każdego z nich uruchom następującą funkcję:
    • (1!:1)@(<`3:@.('-'-:])): jeśli elementem był -, przeczytaj zawartość deskryptora pliku 3(stdin), w przeciwnym razie przeczytaj zawartość pliku nazwanego przez ten element. Jeśli to się nie powiedzie (tzn. Plik nie istnieje), powyższy kod go złapie i zwróci 1.
    • exit@3:`(... )@.(1e6>#)@(<;.2): podziel ciąg na końcach linii. Jeśli jest 1 000 000 lub więcej linii, zakończ ze statusem 3. Inaczej:
      • ,&TAB@(6&":)&.>@>:@i.@#: wygeneruj liczby dla każdego wiersza, sformatuj je w 6-cyfrowej kolumnie i dodaj a TABna końcu każdego ciągu,
      • ,&.>]: dodaj każdy numer na początku każdej linii.
      • stdout@(,&LF)@;: następnie wypisz całość, a następnie dodatkowy LF.
  • exit: wyjście z wartością zwracaną przez tę funkcję

1

Rubinowy, 76 bajtów

Jeden bajt dla pflagi. Uruchom z ruby -p nl.rb.

BEGIN{x=$*.size-65}
exit 2if$*.size==x
exit 3if$.>999999
$_="%6d"%$.+?\t+$_

Argumenty standardowe lub plikowe są obsługiwane automatycznie przez Ruby. Wychodzi już z kodem 1, jeśli argument pliku nie istnieje. $.to liczba przeczytanych wierszy. $*to argumenty wiersza poleceń, a pliki są usuwane podczas odczytywania plików. pFlaga wykonuje BEGINblok i zawija resztę programu wewnątrz pętli while-dostaje-Print, używając $_jako wejścia / wyjścia.


Specyfikacja mówi, że powinieneś obsłużyć pierwsze 64 wejścia, jeśli podano więcej niż 64, zamiast po prostu poddawać się na początku.
Anders Kaseorg

@AndersKaseorg naprawione.
daniero

1

Perl, 125 122 bajtów

@a=@ARGV;for(@a){$i++>63&&exit 2;($_ eq '-'or-e$_)or next;@ARGV=$_;while(<>){$c>1E6-2&&exit 3;printf"%5d\t%s",++$c,$_}say}

Nie spełnia to specyfikacji dotyczącej maksimum 64 argumentów i statusu wyjścia.
Anders Kaseorg,

@AndersKaseorg Naprawiono!
Gowtham

0

C 362 359

Tylko dla zabawy. ;-) Działa z kanałami liniowymi LF lub CR / LF.

#include<stdio.h>
#define R return
#define P printf(
e,t,l;void*f;r(){P"% 6d\t",++l);for(;(t=fgetc(f))^-1&&l<1000000;){if(ferror(f))R 1;P"%c",t);if(t==10)P"% 6d\t",++l);}P"\n");R l<1000000?0:3;}main(int c,char**v){e=c>65?2:0;for(++v;*v||c<2;++v){t=c<2||!strcmp(*v,"-")?f=stdin,0:!(f=fopen(*v,"rb"));if(t||(t=r()))e=!e|(e>t)?t:e;if(f&&f!=stdin)fclose(f);}R e;}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.