Insist on a new filename

19

0

At runtime, keep prompting for a line of input until the user input is not the name of an existing file or directory or other file system item, relative to the current working directory. Then return/print that last inputted filename. You may assume that all user inputs will be valid filenames.

Pseudo-code 1

myform = new form("GUI")
myform.mytxt = new editfield("")
myform.ok = new button("OK")
repeat
  waitfor(myform.ok,"click")
until not filesystem.exists(myform.mytxt.content)
return(myform.mytxt.content)

Pseudo-code 2

LET TEXT = "."
WHILE HASFILE(TEXT) DO
  TEXT = PROMPT("")
ENDWHILE
RETURN TEXT

Examples of user input which will cause re-prompting when on TIO:

.
..
.env.tio
/
/bin/[
/lost+found

Examples of user input which will return when on TIO:

...
env.tio
../../bin/]
/lost/found

Adám

Posted 2018-03-15T21:41:19.007

Reputation: 37 779

I'm fairly new to code golf here and can't seem to find any information on what counts as a solution. Do I need to include the main() function for languages that require it in a program, or can that be part of the header? Can import statements be a part of the header in TIO, or do they need to be part of the code and count against the byte count? For example, I have this solution: https://goo.gl/8RWNgu but not sure if it the bytes would be legitimate.

– Makotosan – 2018-03-16T15:33:53.507

2

@Makotosan Both functions and full programs are fine, although in the case of functions they need to be reusable. Imports generally need to be included in the byte count.

– Martin Ender – 2018-03-16T15:41:20.270

Answers

7

Batch, 37 bytes

@set/ps=
@if exist %s% %0
@echo %s%

(For some reason current Windows 10 CMD.EXE corrupts the title when it executes the %0.)

Neil

Posted 2018-03-15T21:41:19.007

Reputation: 95 035

7

Mathematica, 33 28 bytes

f:=Input[]/._?FileExistsQ:>f

This assumes Mathematica's notebook environment where we can query input from the user with Input[]. The user input should be an actual string literal, so e.g. "ab/cd.ef" instead of just ab/cd.ef. The upside is that the input can be an arbitrary Mathematica expression that computes the input string.

This defines a symbol f which, when evaluated performs the required computation and ultimately evaluates to the first non-existent user input. It's essentially a nullary function, where we don't have to include ...[] to call it.

We can also save a bunch of bytes over a traditional If expression by making use of the pattern substitution operator /..

Martin Ender

Posted 2018-03-15T21:41:19.007

Reputation: 184 808

This fails if the user inputs the same thing twice – Lukas Lang – 2018-03-15T23:08:17.793

@Mathe172 Good catch, too bad, then I'll have to go with the boring loop. – Martin Ender – 2018-03-15T23:14:38.753

Turns out I don't, and it's even a byte shorter. :) – Martin Ender – 2018-03-16T00:16:40.020

7

Perl 5 -ln, 12 10 bytes

-2 bytes thanks to @DomHastings

#!/usr/bin/perl -ln
-e||1/!say

Try it online!

Ton Hospel

Posted 2018-03-15T21:41:19.007

Reputation: 14 114

1I think -e works without specifying $_, might not work for some cases though I guess... – Dom Hastings – 2018-03-16T10:17:59.173

@DomHastings Should work in all cases. It's documented that $_ is used when there is no argument to -e. – pipe – 2018-03-16T10:38:05.950

@DomHastings Thanks.For some reason I thought -e was an exception but of course it isn't – Ton Hospel – 2018-03-16T10:39:40.690

4

Bash, 29

read f
[ -e $f ]&&$0||echo $f

Digital Trauma

Posted 2018-03-15T21:41:19.007

Reputation: 64 644

Why doesn't this work? – Adám – 2018-03-15T23:40:08.770

@Adám I'm not sure why it doesn't work in TIO. Suffice to say if you save it as a script file and run it, I think it works as expected – Digital Trauma – 2018-03-16T00:06:50.407

1

@Adám This works, the problem you had was that the program was trying to call .code.tio which contains the body of the script, but no information on how to run it. I'm not sure if there is a nice way to work around the shebang or this script needing to be in your path, though.

– FryAmTheEggman – 2018-03-16T01:47:10.463

2

You can get around both (at the cost of two bytes) by changing $0 to . $0. Try it online!. Since . uses relative path names and the current shell.

– Chris – 2018-03-16T07:20:58.237

1What does it think of * as input? – Toby Speight – 2018-05-21T10:31:48.957

4

PowerShell 2 (through 6), 35 bytes

while(Test-Path($x=Read-Host)){};$x

Read-Host waits for input (if given a string as a parameter, uses the string as a prompt). If the provided input is a filename (or folder name) for one that exists, Test-Path returns $true, and the do-nothing block {} executes, and it re-prompts for input. If Test-Path returns $false because the input is not an extant file or folder, the do-nothing block does not execute, and the input name is printed.

Jeff Zeitlin

Posted 2018-03-15T21:41:19.007

Reputation: 213

1Welcome to PPCG! – Martin Ender – 2018-03-16T19:46:43.137

You don't need the semi-colon after the {} to save a byte. – Veskah – 2018-05-22T22:23:31.883

@Veskah - I did in PS2, and it doesn't break PS3+ – Jeff Zeitlin – 2018-05-23T11:17:53.263

Ah, my bad. Didn't test it in 2. – Veskah – 2018-05-23T20:01:40.717

4

C (gcc), 62 bytes

main(){char b[99];while(scanf("%s",b)&&!access(b,0));puts(b);}

Try it online!

main(){
    char b[99]; // Declare buffer b
    while (scanf("%s",b)&&!access(b,0));    // Take one line of input, and test if file is accessible (exists)
    puts (b);   // If doesn't exist, loop ends and print file
}

Arctic Kona

Posted 2018-03-15T21:41:19.007

Reputation: 141

Welcome to PPCG! You can use while(gets(b),!access(b,0)); to save 7 bytes. – Dennis – 2018-05-21T04:03:39.900

3

Python 3, 55 bytes

import glob
s="."
while glob.glob(s):s=input()
print(s)

Try it online!

-4 bytes thanks to ManfP
-6 bytes thanks to Rick Rongen

HyperNeutrino

Posted 2018-03-15T21:41:19.007

Reputation: 26 575

2@Adám terrible misinterpretation, sorry – HyperNeutrino – 2018-03-15T22:57:45.217

You could replace the first input() with "." – ManfP – 2018-03-15T23:00:58.300

1import os and os.path.exists is three bytes shorter. – Jonathan Allan – 2018-03-16T08:47:42.470

1Rick Rongen suggested import glob and while glob.glob(s):... in an edit. – Martin Ender – 2018-03-16T09:59:10.733

@MartinEnder thanks for telling me :) – HyperNeutrino – 2018-03-16T15:13:32.677

@JonathanAllan Thanks; already got it down below -3 with another suggestion :) – HyperNeutrino – 2018-03-16T15:13:46.253

3

Haskell, 76 bytes

import System.Directory
f=do x<-getLine;b<-doesPathExist x;last$pure x:[f|b]

Try it online!

Returns IO x where x is the inputted name of the file that does not exist.

Ungolfed

import System.Directory

insist = do { file <- getLine;
              exists <- doesPathExist file;
              if exists then insist else pure file }

totallyhuman

Posted 2018-03-15T21:41:19.007

Reputation: 15 378

3

Funky, 40 bytes

tryfor)io.open(s=io.read())catchprint(s)

In true funky style, this uses keywords jammed against eachother, unmatching brackets and implicit keywords. Cleaned up, this looks like:

try{
    while(true){
        s = io.read()
        io.open(s)
    }
}catch(e){
    print(s)
}

Breakdown

try                                     // Try statement, this one is expected to fail.
   for)                                 // for) is a for loop with no arguments, which is functionally equivilent to a while(true) loop, much like for(;;)
       io.open(                         // Try to open a file relative to the CWD. If this fails to find a file, it will throw an error and escape the try/catch
               s=io.read()              // Read a line from STDIN and store it as s, this will still pass it to the arguments of the call.
                          )
                           catch        // When io.open fails
                                print(s)// Print out the last entered line.

ATaco

Posted 2018-03-15T21:41:19.007

Reputation: 7 898

3

R, 66 51 bytes

while((s=readline())%in%list.files(a=T)){};print(s)

-15 bytes thanks to plannapus

Runs a potentially infinite loop, where on each iteration

  1. A single line of user input is stored in the variable s
  2. We check if the input is in the list of filenames for the working directory (the a=T option for list.files() must be used to pick up things like ..)
  3. If s is in that list, we go to the next iteration; if not, we break the loop and print s.

duckmayr

Posted 2018-03-15T21:41:19.007

Reputation: 441

How about shortening it to while((s=readline())%in%list.files(a=T)){};print(s)? – plannapus – 2018-03-16T13:06:45.733

@plannapus Great idea! Incorporated. – duckmayr – 2018-03-16T13:12:46.110

You're welcome. Also, I didn't think of it right away but functions list.files and dir are synonyms, so you can replace it with dir here. – plannapus – 2018-03-16T13:14:37.550

you could also replace readline() with scan(,'') – JAD – 2018-03-16T14:12:20.510

And print with cat – JAD – 2018-03-16T14:15:21.283

32 bytes: while((s=scan(,''))%in%dir())t;s – rturnbull – 2018-03-16T21:33:07.987

3

C#, 101 bytes

()=>{var s="";try{for(;;System.IO.File.GetAttributes(s=System.Console.ReadLine()));}catch{}return s;}

For each of the 4 valid return values:

Ungolfed

() =>
{
    var s = "";
    try
    {
        for(;; System.IO.File.GetAttributes(s = System.Console.ReadLine()));
    }
    catch {}
    return s;
}

Explanation

relies on the fact that File.GetAttributes() throws an exception if file system object specified in its argument doesn't exist.

Richard II

Posted 2018-03-15T21:41:19.007

Reputation: 151

2

Powershell 3.0, 75 bytes

$x=1;while($x){$i=Read-Host;$x=Test-Path("$PSScriptRoot\$i")};Write-Host $i

First attempt; I'm sure there are a few optimizations I could make.

A slightly more readable form:

$x=1;                                                                       # Make sure we enter our while loop.
     while($x){                                                             # While we keep getting file names,                   
               $i=Read-Host;                                                # Get input from the user
                            $x=Test-Path("$PSScriptRoot\$i")};              # Combine our path with the user input, and see if it already exists.
                                                              Write-Host $i # Return the final (valid) file name.

Arkitekt

Posted 2018-03-15T21:41:19.007

Reputation: 131

Wouldn't it work even without $PSScriptRoot\? – Adám – 2018-03-16T06:46:46.307

Welcome to PPCG! A few quick golfs -- you can use a for loop instead, which allows you to move the initialization into the loop constructor for($x=1;$x){...}. Secondly, you can get rid of the Write-Host since there's an implicit Write-Output at program completion for anything left on the pipeline, so just leaving $i there will suffice. – AdmBorkBork – 2018-03-16T12:49:08.477

See my solution below; I've halved your byte count. – Jeff Zeitlin – 2018-03-16T19:38:59.713

@Adám: Maybe! I hadn't actually considered that. :P

AdmBorkBork Thank you! I spent a long time lurking. Those are good ideas; the implicit output didn't even cross my mind... – Arkitekt – 2018-03-17T18:53:26.643

2

Java 9, 87 bytes

v->{String s;for(;new java.io.File(s=System.console().readLine()).exists(););return s;}

Ungolfed

TIO's JVM apparently has no system Console, so it's not testable there (see System.console()).

import java.util.function.*;
class Main {
  public static void main(String[] args) {
    Function<Void,String> f =


v->{
  String s;
  for(;new java.io.File(s=System.console().readLine()).exists(););
  return s;
}


;
    System.out.println(f.apply(null));
  }
}

Olivier Grégoire

Posted 2018-03-15T21:41:19.007

Reputation: 10 647

2

JavaScript (Node.js), 158 118 bytes

require('readline').createInterface({input:process.stdin}).on('line',s=>require('fs').existsSync(s)||--console.log(s))

Try it online!

Credit to @ConorO'Brien for coming up with shorter version. Inlined objects instead of using consts and utilizing error exit condition instead of explicitly exiting.

Makotosan

Posted 2018-03-15T21:41:19.007

Reputation: 503

1

Nice answer so far, but there's room for potential. You can golf this approach in a few ways: you can omit both const, and you can also replace each variable with it's definition. Then, instead of using s=>{if(...){...}}, you could use s=>require('fs').existsSync(s)||process.exit(console.log(s)). Additionally, you can exit with an error, so you can write the lambda as s=>require('fs').existsSync(s)||--console.log(s). Try it online!

– Conor O'Brien – 2018-03-17T02:02:58.447

Great ideas! Thanks! – Makotosan – 2018-03-17T03:59:22.583

1

Clean, 100 94 bytes

import System.IO,System.File
Start w#(s,w)=evalIO getLine w
#(b,w)=fileExists s w
|b=Start w=s

Try it online!

single-expression version:

import System.IO,System.File
Start w=(\(s,v)=(\(b,w)|b=Start w=s)(fileExists s v))(evalIO getLine w)

Try it online!

Οurous

Posted 2018-03-15T21:41:19.007

Reputation: 7 916

1

APL (Dyalog), 17 bytes

{⍞}⍣{~⎕NEXISTS⍺}⍬

Try it online!

Uriel

Posted 2018-03-15T21:41:19.007

Reputation: 11 708

Returns the second non-existing input, (the_prev_wasnt_filename instead of env.tio). Change and then you can get rid of '.' too. – Adám – 2018-03-16T06:52:16.713

1

Perl 6, 39 bytes

my$f=".";while $f.IO.e {$f=get};say $f;

This works in the REPL, but it doesn't seem to work properly in TIO.

mattchuranu

Posted 2018-03-15T21:41:19.007

Reputation: 11

What about say first !*.IO.e,lines (23 bytes)?

– nwellnhof – 2018-03-17T14:43:11.607

The above probably blocks indefinitely when run on the command line, but something like {}while ($_=get).IO.e;.say should work. – nwellnhof – 2018-03-17T15:03:40.077

1

PHP, 43 bytes

<?for(;file_exists($f=readline()););echo$f;

Run as CLI. Quite easy to understand.

tsh

Posted 2018-03-15T21:41:19.007

Reputation: 13 072

1

Ruby, 40 39 37 bytes

{}while File.exist? gets.chomp
$><<$_

Try it online!

Cristian Lupascu

Posted 2018-03-15T21:41:19.007

Reputation: 8 369

1

Kotlin, 67 bytes

val f={var s="."
while(java.io.File(s).exists()){s=readLine()!!}
s}

Try it online!

Makotosan

Posted 2018-03-15T21:41:19.007

Reputation: 503

1

Attache, 35 bytes

{While[FileExists[x:=Prompt[]],0]x}

Try it online!

Alternative solutions

35 bytes: {If[FileExists[x:=Prompt[]],$[],x]}, recursive function.

37 bytes: {NestWhile[p:=Prompt,p[],FileExists]}, iterative function.

Conor O'Brien

Posted 2018-03-15T21:41:19.007

Reputation: 36 228

1

Min, 38 bytes

"." :a (a exists?) ("" ask @a) while a

Leaves last entered filename on the stack.

Explanation

"."         ; Put . on the stack. Every directory should contain this...
:a          ; Assign to a
(a exists?) ; A quot that checks if a exists in current directory
("" ask @a) ; Read line from stdin, assign to a
while       ; Do the second quote while the first leaves true on the stack
a           ; Leave a on the stack

Panda0nEarth

Posted 2018-03-15T21:41:19.007

Reputation: 111

0

SmileBASIC, 27 bytes

INPUT S$EXEC!CHKFILE(S$)?S$

12Me21

Posted 2018-03-15T21:41:19.007

Reputation: 6 110