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.
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.
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.
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 from 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'$ || _
prompt= '──────────enter four parameters (all 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 lines···*/ /* [↑] read the entire input stream. */
?= /*define the phrase to be null (so far)*/
do ask=0; bad=1; say prompt; pull y /*get just─in─time positional numbers. */ if y='QUIT' then exit 0 /*the user wants out of this loop, exit*/ y=space( translate(y, $, $ || xrange() )) /*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 /*validate parms {positive integers}.*/ if datatype(a.k,'W') & a.k>0 then iterate /*found a bad parameter. */ say 'parameter ' k " is missing or not a positive integer: " a.k iterate ask /*go and ask for another set of parms. */ a.k=a.k / 1 /*normalize the user's parm (number). */ 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.