Numérotation des lignes - Implémenter NL


13

Votre tâche consiste à implémenter un programme similaire à l' nloutil de ligne de commande des utilitaires principaux GNU.

Les failles standard sont interdites.

Vous ne pouvez utiliser aucune fonction, programme ou utilitaire intégré ou externe pour numéroter les lignes d'un fichier ou d'une chaîne, comme nllui-même ou la =commande dans GNU sed.

spécification

Contribution

Le programme accepte les noms de fichiers comme arguments. Votre code n'a pas besoin d'être multiplateforme; le format de nom de fichier du système d'exploitation exécutant le code doit être utilisé, c'est-à-dire que si vous êtes sous Windows, le séparateur de répertoire peut être \ou /.

Vous devez pouvoir prendre 64 fichiers d'entrée, y compris -s'il est spécifié. Si plus de 64 sont donnés, ne gérez que les 64 premiers.

Dans la liste des noms de fichiers, -représente l'entrée standard.

Si des noms de fichiers sont donnés, lisez les fichiers dans l'ordre où ils sont donnés et concaténez leur contenu, en insérant une nouvelle ligne entre chacun et à la fin. Si vous ne pouvez pas lire un ou plusieurs des noms de fichiers (parce que le fichier n'existe pas ou que vous ne disposez pas des autorisations de lecture pour celui-ci), ignorez-les. Si tous les noms de fichiers spécifiés ne sont pas valides, ne rien produire.

Si aucun nom de fichier n'est donné, lisez à partir de l'entrée standard. Lire uniquement à partir de l'entrée standard si aucun nom de fichier n'est donné ou si -est donné.

Production

Le programme sortira, en sortie standard, l'entrée avec des lignes numérotées ainsi (vous pouvez supposer que l'entrée a \n, \r\nou des \rfins de ligne; choisissez celle qui vous convient, mais spécifiez laquelle):

<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 caractères d'espace sont alloués pour le numéro de ligne, et il est inséré à la fin de ces caractères; les autres deviennent des espaces (par exemple, 1auront 5 espaces de tête, 22auront 4 espaces de tête, ...). Si l'entrée est suffisamment longue, vous finirez par manquer d'espace pour le numéro de ligne, à la ligne 999999. Vous ne devez rien sortir après la ligne 999999.

Si l'entrée est vide, rien ne sort.

Statut de sortie

Les nombres inférieurs sont prioritaires: si des erreurs 1 et 2 se sont produites, quittez avec le statut 1.

Quittez avec l'état 0 si l'entrée a été reçue avec succès et si les lignes ont été numérotées et sorties avec succès.

Quittez avec le statut 1 si un ou plusieurs des fichiers spécifiés sur la ligne de commande sont introuvables ou ne peuvent pas être lus.

Quittez avec le statut 2 si trop de fichiers (plus de 64) ont été donnés.

Quittez avec l'état 3 si l'entrée était trop longue (plus de 999999 lignes). \

Notation

C'est le code-golf - le programme le plus court gagne!

Je peux ajouter des bonus plus tard pour la mise en œuvre de certaines options nl. Il n'y a pas de bonus pour le moment.


La numérotation des lignes est-elle continue ou courte, elle se "réinitialise" elle-même pour chaque fichier individuel?
britishtea

@britishtea C'est continu

1
Est-il donc nécessaire d'utiliser node si nous voulons soumettre quelque chose en js? Ou pouvons-nous utiliser la fonction args ou prompt()pour émuler le programme args et stdin?
DankMemes

1
Fichiers binaires? Codage? Marqueurs Unicode?
edc65

Réponses:


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
Vous pouvez ifraccourcir un peu vos expressions si vous utilisez des expressions arithmétiques, par exemple(($#>64))&&s=2
Digital Trauma

2
@DigitalTrauma J'ai appris une chose!
Sammitch

1
Vous pouvez remplacer s=0;(($#>64))&&s=2par s=$[2*($#>64)], (($s==0))||par ((s))&&et l' ifinstruction par [ -f "$f" ]||s=1.
Dennis


2
awkconcatènera également si plusieurs fichiers sont passés, donc cela compte officiellement comme une utilisation inutile de cat ;-). Au lieu de cela, je pense que cela fonctionnera:awk '...' $@
Digital Trauma

2

Rubis, 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

Je pense STDINest aliasé $<.
Martin Ender

Il s'agit d'un alias pour ARGF, qui lira également le reste des fichiers fournis comme arguments. Je pense que cela peut être approfondi en utilisant en ARGFquelque sorte (il semble même reconnaître "-"comme étant stdin).
britishtea

britishteanl: 4: in block in <main>': undefined method [] 'for # <Enumerator: 0x000006002980c8> (NoMethodError) from britishteanl: 2: in each' from britishteanl:2:in <main>' - qu'est-ce qui ne va pas? Je l'ai couru en tant queruby britishteanl folder/filename

Je soupçonne que c'est une différence dans la version Ruby. J'ai exécuté l'exemple sur Ruby 2.0.0 et Ruby 2.1.2 sans problème. Quelle version utilisez-vous?
britishtea

2

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

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

Débarqué:

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

Important à savoir:

  • -penveloppe le programme donné avec -elewhilecontinue boucle /
  • BEGIN le code sera exécuté avant la partie principale (implicite)
  • le test de fichier -réchoue également si le fichier n'existe pas !-eet passe par défaut au test$_ , donné implicitement dansmap { ... } @ARGV
  • $. contient le numéro de ligne actuel
  • le reste devrait être explicite;)

Excellente réponse et bienvenue dans Programming Puzzles et Code Golf! Vous pouvez peut-être modifier pour ajouter une explication sur le fonctionnement de votre code.
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

Je pense que votre code manque actuellement le -for sys.stdin. Une solution possible pourrait être quelque chose comme ça fh=sys.stdin if f=='-' else open(f)et ensuite aller avec x=fh.readline()?! Malheureusement, cela ne le raccourcit pas. :)
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

Explication:

  • ]`(]&<&'-')@.(0=#)2}.ARGV: Récupérez les arguments de la ligne de commande et supprimez les deux premiers (car ce sont l'interpréteur et le nom du fichier de script). Si la liste résultante est vide, retournez ['-'](c'est-à-dire, comme si l'utilisateur avait passé seulement -), sinon retournez la liste inchangée.
  • (... ::1:): si la fonction interne échoue, retournez 1, sinon retournez tout ce que la fonction interne a renvoyé.
  • ((2*64<#)[... ): évalue la fonction interne et jette le résultat. Ensuite, si la longueur de la liste transmise n'était pas supérieure à 64, retournez 0, sinon retournez 2.
  • &.>@;@{.@(_64&(<\)): obtenir la plupart des 64éléments de la liste et pour chacun d'eux exécuter la fonction suivante:
    • (1!:1)@(<`3:@.('-'-:])): si l'élément était -, lisez le contenu du descripteur de fichier 3(stdin), sinon lisez le contenu du fichier nommé par cet élément. Si cela échoue (c'est-à-dire que le fichier n'existe pas), le code ci-dessus le rattrapera et le retournera 1.
    • exit@3:`(... )@.(1e6>#)@(<;.2): divise la chaîne sur ses fins de ligne. S'il y a 1 000 000 lignes ou plus, quittez avec le statut 3. Autrement:
      • ,&TAB@(6&":)&.>@>:@i.@#: générer les nombres pour chaque ligne, les formater dans une colonne à 6 chiffres et ajouter un TABà la fin de chaque chaîne,
      • ,&.>]: ajoutez chaque numéro au début de chaque ligne.
      • stdout@(,&LF)@;: puis sortez le tout, suivi d'un extra LF.
  • exit: exit avec la valeur de retour de cette fonction

1

Rubis, 76 octets

Un octet pour le pdrapeau. Courez avec ruby -p nl.rb.

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

Les arguments stdin ou file sont gérés automatiquement par Ruby. Il se ferme déjà avec le code 1 si un argument de fichier n'existe pas. $.est le nombre de lignes lues. $*correspond aux arguments de la ligne de commande et les fichiers sont extraits lors de la lecture des fichiers. L' pindicateur exécute le BEGINbloc et enveloppe le reste du programme dans une boucle while-gets-print, en utilisant $_comme entrée / sortie.


La spécification indique que vous devez gérer les 64 premières entrées si elles sont données plus de 64, plutôt que de simplement abandonner au début.
Anders Kaseorg

@AndersKaseorg corrigé.
daniero

1

Perl, 125 122 octets

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

Cela ne satisfait pas la spécification concernant le maximum de 64 arguments et l'état de sortie.
Anders Kaseorg

@AndersKaseorg Fixed!
Gowtham

0

C, 362 359

Juste pour le plaisir. ;-) Fonctionne avec les sauts de ligne 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;}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.