Just in time processing on a character stream

From Rosetta Code
Just in time processing on a character stream is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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++[edit]

Text used to encode:The Raven - by E.A.Poe

 
#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;
}
 
Output:
Silence-Dogood.

C#[edit]

Translation of: D
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();
}
}
}
Output:
Silence-Dogood.

D[edit]

Translation of: Kotlin
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);
}
Output:
Silence-Dogood.

Go[edit]

Translation of: C++
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)
}
}
Output:
Silence-Dogood.

Java[edit]

Translation of: Kotlin
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);
}
}
Output:
Silence-Dogood.

Kotlin[edit]

Translation of: C++
// 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)
}
Output:
Silence-Dogood.

REXX[edit]

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.  

/*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. */
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[edit]

Works with: Tcl version 8.6
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)
}
}

Demonstration of use:

[JustInTimeStreamExtract new] stream [open "sample.txt"]

zkl[edit]

Translation of: C++
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") });
}
input:=getUserInput();
// our code pad is: http://paulo-jorente.de/text/theRaven.txt
FlyBy.decode("theRaven.txt",input);
Output:
Silence-Dogood.