Numeración de línea - implementar nl

13

Su tarea es implementar un programa similar a la nlherramienta de línea de comandos de las utilidades principales de GNU.

Las lagunas estándar están prohibidas.

No puede utilizar ninguna función, programa o utilidad incorporada o externa para numerar las líneas de un archivo o cadena, como nlsí mismo o el =comando en GNU sed.

Especificación

Entrada

El programa acepta nombres de archivo como argumentos. Su código no tiene que ser multiplataforma; se debe usar el formato de nombre de archivo del sistema operativo que ejecuta el código, es decir, si está en Windows, el separador de directorio puede ser \o /.

Debe poder tomar 64 archivos de entrada, incluso -si se especifica. Si se dan más de 64, solo maneje los primeros 64.

En la lista de nombres de archivo, -representa la entrada estándar.

Si se dan nombres de archivo, lea los archivos en el orden en que se dan y concatene sus contenidos, insertando una nueva línea entre cada uno y al final. Si no puede leer uno o más de los nombres de archivo (porque el archivo no existe o no tiene permisos de lectura), ignórelos. Si todos los nombres de archivo especificados no son válidos, no envíe nada.

Si no se dan nombres de archivo, lea desde la entrada estándar. Solo lea desde la entrada estándar si no se dan nombres de archivo o si -se da.

Salida

La salida se programa, a la salida estándar, la entrada con las líneas numeradas de este modo (Usted puede asumir que la entrada tiene \n, \r\no \rlos finales de línea, recoger lo que sea conveniente para usted, pero especificar cuál):

<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>
...
...

Se asignan 6 caracteres de espacio para el número de línea, y se inserta al final de estos caracteres; el resto se convierte en espacios (por ejemplo 1, tendrá 5 espacios iniciales, 22tendrá 4 espacios iniciales, ...). Si la entrada es suficientemente larga, eventualmente se quedará sin espacio para el número de línea, en la línea 999999. No debe generar nada después de la línea 999999.

Si la entrada está vacía, no genera nada.

Estado de salida

Los números más bajos tienen prioridad: si se encontraron los errores 1 y 2, salga con el estado 1.

Salga con el estado 0 si la entrada se recibió con éxito y las líneas se numeraron y salieron correctamente.

Salga con el estado 1 si uno o más de los archivos especificados en la línea de comando no se encontraron o no se pudieron leer.

Salga con el estado 2 si se proporcionaron demasiados archivos (más de 64).

Salga con el estado 3 si la entrada fue demasiado larga (más de 999999 líneas). \

Puntuación

Este es el código de golf: ¡el programa más corto gana!

Puedo agregar bonos más tarde para implementar ciertas opciones que nltiene. No hay bonos por el momento.


fuente
¿La numeración de línea es continua o corta y se "restablece" para cada archivo individual?
britishtea
@britishtea Es continuo
1
Entonces, ¿es necesario usar node si queremos enviar algo en js? ¿O podemos usar args de función o prompt()emular args y stdin de programa?
DankMemes
1
Archivos binarios? Codificación? Marcadores Unicode?
edc65

Respuestas:

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)}' $@
Sammitch
fuente
1
Puede hacer que sus ifexpresiones sean un poco más cortas si usa expresiones aritméticas, por ejemplo(($#>64))&&s=2
Digital Trauma
2
@ DigitalTrauma ¡Aprendí una cosa!
Sammitch
1
Puede reemplazar s=0;(($#>64))&&s=2con s=$[2*($#>64)], (($s==0))||con ((s))&&y la ifdeclaración con [ -f "$f" ]||s=1.
Dennis
2
awk también se concatenará si se pasan varios archivos, por lo que esto cuenta oficialmente como un uso inútil de cat ;-). En cambio, creo que esto funcionará:awk '...' $@
Digital 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
britishtea
fuente
Creo que STDINtiene un alias $<.
Martin Ender
Es un alias para ARGF, que también se leerá del resto de los archivos dados como argumentos. Creo que esto se puede reducir aún más usando de ARGFalguna manera (incluso parece reconocer "-"que es estándar).
britishtea
britishteanl: 4: en block in <main>': undefined method [] 'para # <Enumerator: 0x000006002980c8> (NoMethodError) de britishteanl: 2: en each' from britishteanl:2:in <main>' - ¿qué pasa? Lo ejecuté comoruby britishteanl folder/filename
Sospecho que es una diferencia en la versión Ruby. Ejecuté la muestra en Ruby 2.0.0 y Ruby 2.1.2 sin problemas. Qué versión estás usando?
britishtea
2

Perl, 84 + 2 ( -pl) = 86 bytes

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

Despachado:

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

Importante saber:

  • -pajusta el programa dado -een el bucle while/continue
  • BEGIN el código se ejecutará antes de la parte principal (implícita)
  • la prueba de archivo -rtambién falla si el archivo no existe !-ey el valor predeterminado es prueba $_, dado implícitamente enmap { ... } @ARGV
  • $. contiene el número de línea actual
  • el descanso debe explicarse por sí mismo;)
dbr
fuente
¡Gran respuesta, y bienvenido a Programming Puzzles y Code Golf! Quizás podría editar para agregar una explicación de cómo funciona su código.
wizzwizz4
1

pitón 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
Sammitch
fuente
Creo que a su código le falta actualmente el -for sys.stdin. ¡Una posible solución podría ser algo así fh=sys.stdin if f=='-' else open(f)y luego ir con x=fh.readline()?! Lamentablemente, no lo hace más corto. :)
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

Explicación:

  • ]`(]&<&'-')@.(0=#)2}.ARGV: Obtenga los argumentos de la línea de comandos y elimine los dos primeros (porque esos son el intérprete y el nombre del archivo de script). Si la lista resultante está vacía, regrese ['-'](es decir, como si el usuario hubiera pasado solamente -), de lo contrario, devuelva la lista sin cambios.
  • (... ::1:): si la función interna falla, devuelve 1, de lo contrario devuelve lo que devuelve la función interna.
  • ((2*64<#)[... ): evalúa la función interna y tira el resultado. Luego, si la longitud de la lista aprobada no fue mayor que 64, devuelva 0, de lo contrario devuelva 2.
  • &.>@;@{.@(_64&(<\)): obtenga la mayoría de los 64elementos de la lista y para cada uno de ellos ejecute la siguiente función:
    • (1!:1)@(<`3:@.('-'-:])): si el elemento era -, lea el contenido del descriptor de archivo 3(stdin), de lo contrario lea el contenido del archivo nombrado por ese elemento. Si esto falla (es decir, el archivo no existe), el código anterior lo detectará y devolverá 1.
    • exit@3:`(... )@.(1e6>#)@(<;.2): divide la cadena en sus terminaciones de línea. Si hay 1.000.000 o más líneas, salga con el estado 3. De otra manera:
      • ,&TAB@(6&":)&.>@>:@i.@#: genera los números para cada línea, los formatea en una columna de 6 dígitos y agrega un TAB al final de cada cadena,
      • ,&.>]: agrega cada número al frente de cada línea.
      • stdout@(,&LF)@;: luego muestra todo, seguido de un extra LF.
  • exit: salir con el valor de retorno de esa función
marinus
fuente
1

Ruby, 76 bytes

Un byte para la pbandera. Corre con ruby -p nl.rb.

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

Los argumentos estándar o de archivo son manejados automáticamente por Ruby. Ya sale con el código 1 si no existe un argumento de archivo. $.es el número de líneas que se han leído. $*son los argumentos de la línea de comandos, y los archivos se eliminan a medida que se leen. La pbandera ejecuta el BEGINbloque y envuelve el resto del programa dentro de un ciclo while-gets-print, utilizándolo $_como entrada / salida.

daniero
fuente
La especificación dice que debe manejar las primeras 64 entradas si se le dan más de 64, en lugar de simplemente darse por vencido al principio.
Anders Kaseorg
@AndersKaseorg corregido.
daniero
1

Perl, 125 122 bytes

@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}
Gowtham
fuente
Esto no satisface la especificación sobre el máximo de 64 argumentos y el estado de salida.
Anders Kaseorg
@AndersKaseorg Fixed!
Gowtham
0

C, 362 359

Solo por el gusto de hacerlo. ;-) Funciona con avances de línea LF o 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;}
owacoder
fuente