20
\$\begingroup\$

The puppy utility takes a filename as input, and does exactly what you would expect a puppy to do: it shreds it!

puppy

How to shred

  1. Rip out the pages with your teeth Split the input file on newlines.
  2. Tear up each page with your paws For each line (not including the linefeed), pick a random integer n such that 2 <= n <= # of characters in the line. Split the line into n non-empty non-overlapping substrings of random length.
  3. Scatter the shreds all over the floor Output each substring of each line to a unique random filename ([a-zA-Z0-9] only, any consistent extension including none, length 1 <= n <= 12) in the current directory. Overwriting pre-existing files within the current directory (including the input file, if it is in the current directory) is acceptable, as long as it does not interfere with your submission running.

Clarifications

  • There will never be an input where it is possible to use up all of the possible filenames.
  • Files will consist of only printable ASCII (ordinals 32-127) and linefeeds, and will use UNIX/Linux-style line endings (LF, not the Windows-style CRLF).
  • A single trailing newline in output files is acceptable as long as every output file has a trailing newline, but is not required. You may choose whether or not the input file contains a trailing newline.
  • Each line in the input will contain at least 2 characters.
  • The random values chosen must be chosen from a uniform random distribution on the given set of possible values.

Iff your language is unable to perform file I/O, you may instead take the input file's contents as input, and output pairs of strings representing the output filename and the text for that file. However, your submission will not be eligible for winning.

Example

Reference implementation:

#!/usr/bin/env python3

import random
import string
import sys

fname = sys.argv[1]
with open(fname) as f:
  txt = f.read().rstrip().split('\n')

for line in txt:
  n = random.randint(2, len(line))-1
  idxs = [0]+random.sample(range(1, len(line)), n)+[len(line)]
  idxs.sort()
  splits = []
  for i in range(0, len(idxs)-1):
    splits.append(line[idxs[i]:idxs[i+1]])
  ofnames = []
  for s in splits:
    flen = random.randint(1, 10)
    ofname = ''
    while ofname == '' or ofname in ofnames:
      ofname = ''
      for i in range(flen):
        ofname += random.choice(string.ascii_letters+string.digits)
    ofnames.append(ofname)
    with open(ofname, 'w') as f:
      f.write(s)

Example run:

$ cat bestsong.txt
Never gonna give you up
Never gonna let you down
Never gonna run around
And desert you!

$ puppy bestsong.txt

$ ls
8675309
a
bestsong.txt
cSdhg
Dq762
jq7t
ret865
rick4life
weu767g
xyzzy

$ cat 8675309
esert you!

$ cat a
Never gonna let you down

$ cat cSdhg
ive y

$ cat Dq762
And d

$ cat jq7t
Never gonna g

$ cat ret865
run arou

$ cat rick4life
Never gonna 

$ cat weu767g
nd

$ cat xyzzy
ou up
\$\endgroup\$
3
\$\begingroup\$

PowerShell v2+, 215 211 bytes

nal d Get-Random;gc $args[0]|%{$b=d(0..($l=($t=$_).length)) -C(d(2..$l));$b=$b+0+$l|select -u|sort;0..($b.count-2)|%{-join($t[$b[$_]..($b[$_+1]-1)])}}|%{$_>(-join[char[]](d(48..57+65..90+97..122) -c(d(1..12))))}

Requires v2 or newer since v1 didn't have Get-Random available.
Edit -- saved 4 bytes by using char-array casting instead of individually casting each letter

Somewhat Ungolfed

Get-Content $args[0]|ForEach-Object{
  $t=$_
  $l=$t.length
  $b=Get-Random(0..$l) -Count(Get-Random(2..$l))
  $b=$b+0+$l|Select-Object -unique|Sort-Object
  0..($b.count-2)|ForEach-Object{
    -join($t[$b[$_]..($b[$_+1]-1)])
  }
}|ForEach-Object{
  $_>(-join[char[]](Get-Random(48..57+65..90+97..122) -count(Get-Random(1..12))))
}

Explanation

Starts with setting d as a New-Alias for Get-Random, so we don't have to type out Get-Random each time we're using it (a lot). We then Get-Content of our input $args and pipe those through a loop with |%{...}. Note that Get-Content will by default split on newlines (either CRLF or just LF), so we don't need to do anything additional there.

Each iteration of the loop, we start with formulating the slices this line is going to be Ginsu'd into. Set $t equal to the line we're working with, and $l equal to its length, then construct a collection from (0..$l). This represents all possible character indices in our current line. We then Get-Random from between (2..$l) to determine how many to select, and then get a random number of indices equal to that -count. Store those indices in $b.

We then also add on 0 and $l to $b, so we have the beginning and end of our line guaranteed to be in the collection of indices. Pipe that through to Select-Object with the -unique flag, then pipe to Sort-Object, so our indices are now guaranteed to start with the first character and end with the last character, and some random number in between.

Next, we're looping over all the indices in $b with 0..($b.count-2)|%{...}. Each of those loop iterations, we're slicing $t (our current line of text) and then -joining them together into a string (rather than a char-array). Those get bundled up and left on the pipeline, and we close the outer loop.

So now we have an in-order collection of random slices of each of the lines of text. (Meaning, at this point, if we simply -joined them back together, we'll get the original text minus newlines.) We then pipe that collection through another loop |%{...} and each iteration we're outputting that slice to a file with $_>.... The file is created by taking from 1 to 12 random integers that correspond with ASCII codes for [0-9A-Za-z]. No file will have an extension, and the > command will output a trailing newline by default on every file.

Example

Example run

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.