Numeração de linha - implemento nl

13

Sua tarefa é implementar um programa semelhante à nlferramenta de linha de comando dos utilitários principais do GNU.

As brechas padrão são proibidas.

Você não pode usar nenhuma função, programa ou utilitário interno ou externo para numerar as linhas de um arquivo ou string, como nlele próprio ou o =comando no GNU sed.

Especificação

Entrada

O programa aceita nomes de arquivos como argumentos. Seu código não precisa ser multiplataforma; o formato de nome de arquivo do sistema operacional executando o código deve ser usado, ou seja, se você estiver no Windows, o separador de diretório pode ser \ou /.

Você deve conseguir 64 arquivos de entrada, inclusive -se ele estiver especificado. Se mais de 64 forem fornecidos, lide apenas com os primeiros 64.

Na lista de nomes de arquivos, -representa a entrada padrão.

Se os nomes de arquivos forem fornecidos, leia os arquivos na ordem em que são fornecidos e concatene seu conteúdo, inserindo uma nova linha entre cada um e no final. Se você não conseguir ler um ou mais nomes de arquivos (porque o arquivo não existe ou você não possui permissões de leitura), ignore-os. Se todos os nomes de arquivos especificados forem inválidos, não produza nada.

Se nenhum nome de arquivo for fornecido, leia a partir da entrada padrão. Leia apenas da entrada padrão se nenhum nome de arquivo for fornecido ou se -for fornecido.

Resultado

A saída do programa vontade, para a saída padrão, a entrada com linhas numeradas assim (Você pode supor que a entrada tem \n, \r\nou \rfinal de linha, escolher o que for conveniente para você, mas especificar qual):

<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 caracteres de espaço são alocados para o número da linha e são inseridos no final desses caracteres; o restante se torna um espaço (por exemplo 1, terá 5 espaços à esquerda, 22terá 4 espaços à esquerda, ...). Se a entrada for suficientemente longa, você acabará ficando sem espaço para o número da linha, na linha 999999. Você não deve produzir nada após a linha 999999.

Se a entrada estiver vazia, não produza nada.

Status de saída

Os números mais baixos têm prioridade: se os erros 1 e 2 forem encontrados, saia com o status 1.

Saia com o status 0 se a entrada foi recebida com sucesso e as linhas numeradas e produzidas com sucesso.

Saia com o status 1 se um ou mais dos arquivos especificados na linha de comandos não foram encontrados ou não puderam ser lidos.

Saia com o status 2 se forem fornecidos muitos arquivos (mais de 64).

Saia com o status 3 se a entrada for muito longa (mais de 999999 linhas). \

Pontuação

Este é o código-golfe - o programa mais curto vence!

Posso adicionar bônus mais tarde por implementar certas opções que nlpossuem. Não há bônus no momento.


fonte
A numeração de linha é contínua ou curta e é "redefinida" para cada arquivo individual?
britishtea
@britishtea É contínuo
1
Portanto, é necessário usar o nó se queremos enviar algo em js? Ou podemos usar a função args ou prompt()para emular o programa args e stdin?
DankMemes
1
Arquivos binários? Codificação? Marcadores Unicode?
Edc65

Respostas:

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
fonte
1
Você pode tornar suas ifexpressões um pouco mais curtas se usar expressões aritméticas, por exemplo:(($#>64))&&s=2
Digital Trauma
2
@DigitalTrauma Aprendi uma coisa!
Sammitch 30/09/14
1
Você pode substituir s=0;(($#>64))&&s=2por s=$[2*($#>64)], (($s==0))||com ((s))&&e a ifinstrução por [ -f "$f" ]||s=1.
Dennis
2
awktambém concatenará se forem passados ​​vários arquivos, portanto isso conta oficialmente como um uso inútil de cat ;-). Em vez disso, acho que isso 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
fonte
Eu acho que STDINé um alias para $<.
Martin Ender
É um apelido para ARGF, que também lerá o resto dos arquivos dados como argumentos. Eu acho que isso pode ser melhorado usando de ARGFalguma forma (parece até reconhecer "-"como sendo stdin).
britishtea
britishteanl: 4: in block in <main>': undefined method [] 'para # <Enumerator: 0x000006002980c8> (NoMethodError) de britishteanl: 2: em each' from britishteanl:2:in <main>' - o que há de errado? Eu executei-o comoruby britishteanl folder/filename
Eu suspeito que é uma diferença na versão Ruby. Eu executei a amostra no Ruby 2.0.0 e no Ruby 2.1.2 sem problemas. Qual versão você está 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",$.,$_'

Deparsed:

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:

  • -pquebra o programa fornecido -eno loop while/continue
  • BEGIN código será executado antes da parte principal (implícita)
  • O teste de arquivo -rtambém falha se o arquivo não existir !-ee o padrão é o teste $_, fornecido implicitamente emmap { ... } @ARGV
  • $. mantém o número da linha atual
  • o resto deve ser auto-explicativo;)
dbr
fonte
Ótima resposta e seja bem-vindo à Programação de Puzzles e ao Code Golf! Talvez você possa editar para adicionar uma explicação de como seu código funciona.
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
Sammitch
fonte
Eu acho que o seu código está faltando o -para sys.stdin. Uma solução possível pode ser algo como fh=sys.stdin if f=='-' else open(f)e, em seguida, vá com x=fh.readline()?! Infelizmente, não o torna mais curto. :)
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

Explicação:

  • ]`(]&<&'-')@.(0=#)2}.ARGV: Obtenha os argumentos da linha de comando e remova os dois primeiros (porque esses são o intérprete e o nome do arquivo de script). Se a lista resultante estiver vazia, retorne ['-'](ou seja, como se o usuário tivesse passado apenas -), caso contrário, retorne a lista inalterada.
  • (... ::1:): se a função interna falhar, retorne 1; caso contrário, retorne o que a função interna retornou.
  • ((2*64<#)[... ): avalie a função interna e jogue o resultado fora. Então, se o comprimento da lista passada não for maior que 64, retorne 0, caso contrário retorne 2.
  • &.>@;@{.@(_64&(<\)): obtenha o máximo de 64elementos da lista e, para cada um deles, execute a seguinte função:
    • (1!:1)@(<`3:@.('-'-:])): se o elemento foi -, leia o conteúdo do descritor de arquivo 3(stdin), caso contrário, leia o conteúdo do arquivo nomeado por esse elemento. Se isso falhar (ou seja, o arquivo não existe), o código acima o capturará e retornará 1.
    • exit@3:`(... )@.(1e6>#)@(<;.2): divide a string em suas terminações de linha. Se houver 1.000.000 ou mais linhas, saia com status 3. De outra forma:
      • ,&TAB@(6&":)&.>@>:@i.@#: gere os números para cada linha, formate-os em uma coluna de 6 dígitos e adicione TABa no final de cada sequência,
      • ,&.>]: adicione cada número à frente de cada linha.
      • stdout@(,&LF)@;: em seguida, imprima a coisa toda, seguida de um extra LF.
  • exit: sai com o valor de retorno dessa função
marinus
fonte
1

Ruby, 76 bytes

Um byte para a pbandeira. Corra com ruby -p nl.rb.

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

Os argumentos stdin ou file são tratados automaticamente pelo Ruby. Ele já sai com o código 1 se um argumento de arquivo não existir. $.é o número de linhas que foram lidas. $*são os argumentos da linha de comando e os arquivos são removidos à medida que os arquivos são lidos. O psinalizador executa o BEGINbloco e envolve o restante do programa em um loop while-gets-print, usando $_como entrada / saída.

daniero
fonte
A especificação diz que você deve lidar com as primeiras 64 entradas se tiver mais de 64, em vez de simplesmente desistir no início.
Anders Kaseorg
@AndersKaseorg corrigido.
Daniero 3/10
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
fonte
Isso não atende à especificação referente ao máximo de 64 argumentos e ao status de saída.
Anders Kaseorg
@AndersKaseorg Fixed!
Gowtham
0

C, 362 359

Apenas pela diversão. ;-) Trabalha com alimentações de linha LF ou 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
fonte