Just in time processing on a character stream
Given a stream of characters, presumably (simulated) from a keyboard, that contain the separators "formfeed", "linefeed", "tab" and "space" characters. Print out the ith character of the ith tab-field of the ith line of the ith page to reveal a secret password.
Stop processing immediately upon encountering a "!" found uniquely in this i,i,i,i position (least the system self destruct). The "!" may be found/permitted else where however, in which case it should be ignored.
Ideally this can be generalise as follows:
- The separators (formfeed, linefeed, tab, space) provided from a user supplied array and can include additional/alternative separators, e.g. (formfeed, linefeed, ".", "," ," ",...).
- These selection criterial is generalised ith,ith,ith,ith to a boolean function of f(page,line,field,word,...) or f(ith,jth,kth,lth,mth,etc...)
Provide a reasonably interesting message to be decoded, e.g. Silence-Dogood. Your choice.
This task was inspired by the movie "National Treasure" with refers to a "book cipher".
C++
Text used to encode:The Raven - by E.A.Poe <lang cpp>
- include <vector>
- include <iostream>
- include <fstream>
- include <sstream>
typedef struct {
int s[4];
}userI;
class jit{ public:
void decode( std::string& file, std::vector<userI>& ui ) { std::ifstream f( file.c_str(), std::ios_base::in ); fileBuffer = std::string( ( std::istreambuf_iterator<char>( f ) ), std::istreambuf_iterator<char>() ); f.close(); for( std::vector<userI>::iterator t = ui.begin(); t != ui.end(); t++ ) { if( !decode( ( *t ).s ) ) break; } std::cout << "\n\n"; }
private:
bool decode( int* ui ) { int l = 0, t = 0, p = 0, c = 0, a = 0; for( std::string::iterator i = fileBuffer.begin(); i != fileBuffer.end(); i++ ) { if( p == ui[0] && l == ui[1] && t == ui[2] && c == ui[3] ) { if( *i == '!' ) return false; std::cout << *i; return true; } if( *i == '\n' ) { l++; t = c = 0; } else if( *i == '\t' ) { t++; c = 0; } else if( *i == '\f' ) { p++; l = t = c = 0; } else { c++;} } return false; } std::string fileBuffer;
}; void getUserInput( std::vector<userI>& u ) {
std::string h = "0 18 0 0 0 68 0 1 0 100 0 32 0 114 0 45 0 38 0 26 0 16 0 21 0 17 0 59 0 11 " "0 29 0 102 0 0 0 10 0 50 0 39 0 42 0 33 0 50 0 46 0 54 0 76 0 47 0 84 2 28"; //std::getline( std::cin, h ); std::stringstream ss( h ); userI a; int x = 0; while( std::getline( ss, h, ' ' ) ) { a.s[x] = atoi( h.c_str() ); if( ++x == 4 ) { u.push_back( a ); x = 0; } }
} int main( int argc, char* argv[] ) {
std::vector<userI> ui; getUserInput( ui );
jit j; j.decode( std::string( "theRaven.txt" ), ui ); return 0;
} </lang>
- Output:
Silence-Dogood.
C#
<lang csharp>using System; using System.Collections.Generic; using System.Linq;
namespace JustInTimeProcessing {
struct UserInput { public UserInput(string ff, string lf, string tb, string sp) { FormFeed = (char)int.Parse(ff); LineFeed = (char)int.Parse(lf); Tab = (char)int.Parse(tb); Space = (char)int.Parse(sp); }
public char FormFeed { get; } public char LineFeed { get; } public char Tab { get; } public char Space { get; } }
class Program { static List<UserInput> GetUserInput() { string h = "0 18 0 0 0 68 0 1 0 100 0 32 0 114 0 45 0 38 0 26 0 16 0 21 0 17 0 59 0 11 " + "0 29 0 102 0 0 0 10 0 50 0 39 0 42 0 33 0 50 0 46 0 54 0 76 0 47 0 84 2 28"; return h.Split(' ') .Select((x, idx) => new { x, idx }) .GroupBy(x => x.idx / 4) //.Select(g => g.Select(a => a.x)) .Select(g => { var ge = g.Select(a => a.x).ToArray(); return new UserInput(ge[0], ge[1], ge[2], ge[3]); }) .ToList(); }
static void Decode(string filename, List<UserInput> uiList) { string text = System.IO.File.ReadAllText(filename);
bool Decode2(UserInput ui) { char f = (char)0; char l = (char)0; char t = (char)0; char s = (char)0;
foreach (char c in text) { if (f == ui.FormFeed && l == ui.LineFeed && t == ui.Tab && s == ui.Space) { if (c == '!') return false; Console.Write(c); return true; } if (c == '\u000c') { f++; l = (char)0; t = (char)0; s = (char)0; } else if (c == '\n') { l++; t = (char)0; s = (char)0; } else if (c == '\t') { t++; s = (char)0; } else { s++; } }
return false; }
foreach (UserInput ui in uiList) { if (!Decode2(ui)) { break; } } Console.WriteLine(); }
static void Main(string[] args) { var uiList = GetUserInput(); Decode("theraven.txt", uiList);
Console.ReadLine(); } }
}</lang>
- Output:
Silence-Dogood.
D
<lang D>import std.algorithm; import std.array; import std.conv; import std.file; import std.range; import std.stdio;
struct UserInput {
char formFeed; char lineFeed; char tab; char space;
this(string ff, string lf, string tb, string sp) { formFeed = cast(char) ff.to!int; lineFeed = cast(char) lf.to!int; tab = cast(char) tb.to!int; space = cast(char) sp.to!int; }
}
auto getUserInput() {
auto h = "0 18 0 0 0 68 0 1 0 100 0 32 0 114 0 45 0 38 0 26 0 16 0 21 0 17 0 59 0 11 " ~ "0 29 0 102 0 0 0 10 0 50 0 39 0 42 0 33 0 50 0 46 0 54 0 76 0 47 0 84 2 28"; auto ctor = (string[] a) => UserInput(a[0], a[1], a[2], a[3]); return h.split(' ').chunks(4).map!ctor.array;
}
void decode(string fileName, UserInput[] uiList) {
auto text = readText(fileName);
bool decode2(UserInput ui) { char f = 0; char l = 0; char t = 0; char s = 0; foreach (c; text) { if (f == ui.formFeed && l == ui.lineFeed && t == ui.tab && s == ui.space) { if (c == '!') return false; write(c); return true; } if (c == '\u000c') { f++; l=0; t=0; s=0; } else if (c == '\n') { l++; t=0; s=0; } else if (c == '\t') { t++; s=0; } else { s++; } } return false; }
foreach (ui; uiList) { if (!decode2(ui)) break; } writeln;
}
void main() {
auto uiList = getUserInput(); decode("theRaven.txt", uiList);
}</lang>
- Output:
Silence-Dogood.
Go
<lang go>package main
import (
"fmt" "io/ioutil" "log" "strconv" "strings"
)
type userInput struct{ formFeed, lineFeed, tab, space int }
func getUserInput() []userInput {
h := "0 18 0 0 0 68 0 1 0 100 0 32 0 114 0 45 0 38 0 26 0 16 0 21 0 17 0 59 0 11 " + "0 29 0 102 0 0 0 10 0 50 0 39 0 42 0 33 0 50 0 46 0 54 0 76 0 47 0 84 2 28" flds := strings.Fields(h) var uis []userInput var uif [4]int for i := 0; i < len(flds); i += 4 { for j := 0; j < 4; j++ { uif[j], _ = strconv.Atoi(flds[i+j]) } uis = append(uis, userInput{uif[0], uif[1], uif[2], uif[3]}) } return uis
}
func decode(fileName string, uis []userInput) error {
text, err := ioutil.ReadFile(fileName) if err != nil { return err }
decode2 := func(ui userInput) bool { var f, l, t, s int for _, c := range text { if f == ui.formFeed && l == ui.lineFeed && t == ui.tab && s == ui.space { if c == '!' { return false } fmt.Printf("%c", c) return true } switch c { case '\f': f++ l = 0 t = 0 s = 0 case '\n': l++ t = 0 s = 0 case '\t': t++ s = 0 default: s++ } } return true }
for _, ui := range uis { if !decode2(ui) { break } } fmt.Println() return nil
}
func main() {
uis := getUserInput() err := decode("theRaven.txt", uis) if err != nil { log.Fatal(err) }
}</lang>
- Output:
Silence-Dogood.
Java
<lang java>import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate;
public class Processing {
private static class UserInput { private char formFeed; private char lineFeed; private char tab; private char space;
private UserInput(char formFeed, char lineFeed, char tab, char space) { this.formFeed = formFeed; this.lineFeed = lineFeed; this.tab = tab; this.space = space; }
char getFormFeed() { return formFeed; }
char getLineFeed() { return lineFeed; }
char getTab() { return tab; }
char getSpace() { return space; } }
private static List<UserInput> getUserInput() { String h = "0 18 0 0 0 68 0 1 0 100 0 32 0 114 0 45 0 38 0 26 0 16 0 21 0 17 0 59 0 11 " + "0 29 0 102 0 0 0 10 0 50 0 39 0 42 0 33 0 50 0 46 0 54 0 76 0 47 0 84 2 28"; String[] s = h.split(" ");
List<UserInput> uiList = new ArrayList<>(); for (int idx = 0; idx + 3 < s.length; idx += 4) { char c0 = (char) Integer.parseInt(s[idx + 0]); char c1 = (char) Integer.parseInt(s[idx + 1]); char c2 = (char) Integer.parseInt(s[idx + 2]); char c3 = (char) Integer.parseInt(s[idx + 3]);
UserInput userInput = new UserInput(c0, c1, c2, c3); uiList.add(userInput); } return uiList; }
private static void decode(String fileName, List<UserInput> uiList) throws IOException { Path path = Paths.get(fileName); byte[] bytes = Files.readAllBytes(path); String text = new String(bytes, StandardCharsets.UTF_8);
Predicate<UserInput> decode2 = (UserInput ui) -> { char f = 0; char l = 0; char t = 0; char s = 0; char ff = ui.getFormFeed(); char lf = ui.getLineFeed(); char tb = ui.getTab(); char sp = ui.getSpace();
for (char c : text.toCharArray()) { if (f == ff && l == lf && t == tb && s == sp) { if (c == '!') { return false; } System.out.print(c); return true; } switch (c) { case '\u000c': f++; l = 0; t = 0; s = 0; break; case '\n': l++; t = 0; s = 0; break; case '\t': t++; s = 0; break; default: s++; break; } }
return false; };
for (UserInput ui : uiList) { if (!decode2.test(ui)) { break; } } System.out.println(); }
public static void main(String[] args) throws IOException { List<UserInput> uiList = getUserInput(); decode("theRaven.txt", uiList); }
}</lang>
- Output:
Silence-Dogood.
Kotlin
<lang scala>// version 1.2.10
import java.io.File
data class UserInput(val formFeed: Int, val lineFeed: Int, val tab: Int, val space: Int)
fun getUserInput(): List<UserInput> {
val h = "0 18 0 0 0 68 0 1 0 100 0 32 0 114 0 45 0 38 0 26 0 16 0 21 0 17 0 59 0 11 " + "0 29 0 102 0 0 0 10 0 50 0 39 0 42 0 33 0 50 0 46 0 54 0 76 0 47 0 84 2 28" return h.split(' ').chunked(4).map { val (ff, lf, tb, sp) = it UserInput(ff.toInt(), lf.toInt(), tb.toInt(), sp.toInt()) }
}
fun decode(fileName: String, uiList: List<UserInput>) {
val text = File(fileName).readText()
fun decode2(ui: UserInput): Boolean { var f = 0 var l = 0 var t = 0 var s = 0 val (ff, lf, tb, sp) = ui for (c in text) { if (f == ff && l == lf && t == tb && s == sp) { if (c == '!') return false print(c) return true } when (c) { '\u000c' -> { f++; l = 0; t = 0; s = 0 } '\n' -> { l++; t = 0; s = 0 } '\t' -> { t++; s = 0 } else -> { s++ } } } return false }
for (ui in uiList) { if (!decode2(ui)) break } println()
}
fun main(args: Array<String>) {
val uiList = getUserInput() decode("theRaven.txt", uiList)
}</lang>
- Output:
Silence-Dogood.
Python
<lang python>import sys
class UserInput:
def __init__(self,chunk): self.formFeed = int(chunk[0]) self.lineFeed = int(chunk[1]) self.tab = int(chunk[2]) self.space = int(chunk[3])
def __str__(self): return "(ff=%d; lf=%d; tb=%d; sp%d)" % (self.formFeed,self.lineFeed,self.tab,self.space)
def chunks(l,n):
for i in xrange(0, len(l), n): yield l[i:i+n]
def getUserInput():
h = "0 18 0 0 0 68 0 1 0 100 0 32 0 114 0 45 0 38 0 26 0 16 0 21 0 17 0 59 0 11 "\ "0 29 0 102 0 0 0 10 0 50 0 39 0 42 0 33 0 50 0 46 0 54 0 76 0 47 0 84 2 28" ha = h.split() return [UserInput(chunk) for chunk in chunks(ha, 4)]
def decode(filename,uiList):
f = open(filename, "r") text = f.read()
def decode2(ui): f = 0 l = 0 t = 0 s = 0 for c in text: if f == ui.formFeed and l == ui.lineFeed and t == ui.tab and s == ui.space: if c == '!': return False sys.stdout.write(c) return True if c == '\u000c': f=f+1 l=0 t=0 s=0 elif c == '\n': l=l+1 t=0 s=0 elif c == '\t': t=t+1 s=0 else: s=s+1 return False
for ui in uiList: if not decode2(ui): break print
- Main #####
uiList = getUserInput() decode("theRaven.txt", uiList)</lang>
- Output:
Silence-Dogood.
REXX
The input file used by this REXX program only contains one page; it has no FF (formfeed) characters in it),
and the injection of FF characters into the file would be like putting pencil marks into a holy book. ☺
<lang rexx>/*REXX program extracts characters by using a book cipher (that is a text file). */
parse arg iFID . /*obtain optional name of file (book).*/
if iFID== | iFID=="," then iFID= 'JIT.TXT' /*Not specified? Then use the default.*/
$= 'abcdefghijklmnopqrstuvwxyz'; _=$; upper _; $= "0123456789"$ || _; $$=$ || xrange()
prompt= '────────── enter four positive integers or Quit'
pag=1; lin=1; FF= 'c'x /*assume start of page 1, line 1. */
@.= /*read the entire book from the file. */
do while lines(iFID)\==0 /*process lines from input stream(file)*/ _= translate( linein(iFID), , '9'x) /*obtain a single line from input file.*/ if pos(FF, _)\==0 then do; pag=pag+1; lin=1 /*bump page counter; reset line counter*/ end /* [↑] handle finding of FF (formfeed)*/ @.pag.lin= _ /*obtain a single line from input file.*/ lin= lin + 1 /*bump the line counter. */ end /*while*/ /* [↑] read the entire input stream. */
?= /*define the phrase to be null (so far)*/
do ask=0; say prompt; pull y /*get just─in─time positional numbers. */ if abbrev('QUIT', y, 1) then exit 0 /*the user wants out of here, so exit. */ y=space( translate(y, $, $$) ) /*allow any separator the user wants. */ parse var y a.1 a.2 a.3 a.4 /*parse the pertinent parameters. */ if words(y)>4 then do; say 'too many parameters entered.' iterate ask end /*go and try again to obtain the parms.*/ do k=1 for 4; is#= datatype(a.k, 'W') /*validate parms {positive integers}.*/ if is# then a.k= a.k / 1 /*normalize the number (for indexing). */ if is# & a.k>0 then iterate /*found a good parameter? Then skip. */ say 'parameter ' k " is missing or not a positive integer: " a.k iterate ask /*go and ask for another set of parms. */ end /*k*/ /* [↑] done with the validations. */ parse value a.1 a.2 a.3 a.4 with p L w c /*parse parameters for specific names. */ x=substr( word( @.p.L, w), c, 1) /*extract a character from the book. */ if x=='!' then leave /*if the stop char was found, done. */ say right(x '◄─── a letter', 46) /*might as well echo char to terminal. */ ?= ? || x /*append the character to the phrase. */ end /*j*/ /* [↑] display letters found in book. */
say '═════►' ? /*stick a fork in it, we're all done. */</lang>
- input supplied to the console (terminal) by the user in response to the prompts, (the commas are optional):
1, 133, 4, 5 1, 34, 9, 3 1, 1377, 2, 2 1, 4, 8, 4 1, 265, 3, 5 1, 413, 10, 2 1, 10, 12, 1 1, 144, 10, 10 1, 571, 4, 12
- input (abbridged)
──────────enter four parameters (all positive integers) or QUIT 1, 133, 4, 5 ◄■■■■■■■■■ user input (first reponse). s ◄─── a letter ∙ ∙ (some prompts and reponses elided.) ∙ ──────────enter four parameters (all positive integers) or QUIT 1, 571, 4, 12 ◄■■■■■■■■■ user input (ninth reponse). ═════► so─true.
The input file used (the IBM jargon file) in the above REXX program can be found here ───► Just_in_time_processing_on_a_character_stream/REXX/JIT.TXT.
Tcl
<lang tcl>package require Tcl 8.6
oo::class create JustInTimeStreamExtract {
variable map counter counters last constructor {{pageSeparator "\f"} {lineSeparator "\n"} {fieldSeparator "\t"}} {
dict set map $pageSeparator NextPage dict set map $lineSeparator NextLine dict set map $fieldSeparator NextField set counter 1 array set counters {page 0 line 0 field 0 char 0} set last ""
}
method emit {char} {
puts -nonewline $char set last $char
} method finished {} {
if {$last ne "\n"} { puts "" }
}
method stream {{channel stdin} {length 1}} {
try { while 1 { set str [read $channel $length] if {[eof $channel]} break foreach c [split $str ""] { if {[dict exists $map $c]} { my [dict get $map $c] } else { my NextChar $c } } } } trap STOP {} { # Do nothing } my finished
}
method NextPage {} {
incr counters(page) array set counters {line 0 field 0 char 0}
} method NextLine {} {
incr counters(line) array set counters {field 0 char 0}
} method NextField {} {
incr counters(field) set counters(char) 0
} method NextChar {char} {
incr counters(char) if {[my PrintThisOne?]} { if {$char eq "!"} { throw STOP "stop character found" } incr counter my emit $char array set counters {page 0 line 0 field 0 char 0} }
}
method PrintThisOne? {} {
tcl::mathop::== $counter $counters(page) $counters(line) $counters(field) $counters(char)
}
}</lang> Demonstration of use: <lang tcl>[JustInTimeStreamExtract new] stream [open "sample.txt"]</lang>
zkl
<lang zkl>class FlyBy{
fcn decode(file,tuplets){ codePad:=File(file).read().mode(String); // blob of text tuplets.pump(Console.print, _decode.fp1(codePad)); println(); } fcn [private] _decode(idx,codePad){ // idx is (ff,lf,tab,chr) offset info codePad z:=-1; foreach n,c in (idx.zip(T("\f","\n","\t"))){ do(n){ if(Void==(z=codePad.find(c,z+1))) return(Void.Stop); } } if(z==-1) z=0; // (0,0,0,n) try{ return(codePad[z + idx[-1] + 1]) }catch{ return(Void.Stop) } }
}
fcn getUserInput{
// I don't know a user would enter this but we have // a string of 4 item tuplets : (formfeeds, linefeeds, tabs, characters), ... // each tuplet is an offset into a code pad (text) h:="0 18 0 0 0 68 0 1 0 100 0 32 0 114 0 45 0 38 0 26 0 16 0 21 0 17 0 59 0 11 " "0 29 0 102 0 0 0 10 0 50 0 39 0 42 0 33 0 50 0 46 0 54 0 76 0 47 0 84 2 28"; h.split(" ").pump(List,T(Void.Read,3), fcn(ff,lf,t,s){ vm.arglist.apply("toInt") });
}</lang> <lang zkl>input:=getUserInput();
// our code pad is: http://paulo-jorente.de/text/theRaven.txt
FlyBy.decode("theRaven.txt",input);</lang>
- Output:
Silence-Dogood.