B4J Question How to print to console without CRLF?

chikega

Member
Licensed User
I'm following this tutorial on how to make a text adventure game which is purely console based. Huw Collingborne is teaching the course in both C# and Java. In this particular episode, he has started to develop a simple IO console app. He shows how to print the string literal "> " to simulate a console prompt. The user would enter the input on the same line as the cursor prompt, not on the next line which is what B4X.Log does. From my research in the forums, I believe the answer may lie in 'TextWriter.Write' but I'm unable to figure out how to set it up for such a simple problem.

Screenshot 2022-08-06 204203.png

C#:
string? input;
do
{
    Console.Write("> ");   // this is what I would like in B4X, to print without a CRLF 
    userInput = Console.ReadLine();
    Console.WriteLine($"You wrote '{input}'");
} while (input?.ToLower() != "q");
Console.WriteLine("Goodbye!");
Thread.Sleep(2000);
 
Solution
B4X:
Sub Process_Globals
    Private reader As TextReader
End Sub

Sub AppStart (Args() As String)
    Dim System As JavaObject
    System.InitializeStatic("java.lang.System")
    reader.Initialize(System.GetField("in"))
    
    PrintLn("Enter 'q' to end input")
    
    Dim input As String
    Do While(input.ToLowerCase <> "q")
        'Log("> ")       ' <==  I would like to print without CRLF here
        Print("> ")
        input = ReadLine
        PrintLn($"You wrote '${input}'"$)
    Loop
    PrintLn("Goodbye!!!")
End Sub

Sub ReadLine As String
    Return reader.ReadLine
End Sub

Public Sub Print (Prompt As String)
    Private nativeMe As JavaObject = Me
    nativeMe.RunMethod("javaPrint", Array(Prompt))
End Sub

Public Sub PrintLn...

chikega

Member
Licensed User
Here is my code in B4J so far:

B4X:
'Gary Chike      08/05/2022 

#Region Project Attributes 
    #CommandLineArgs:
    #MergeLibraries: True 
#End Region

Sub Process_Globals
    Private reader As TextReader
End Sub

Sub ReadLine As String
    Return reader.ReadLine 
End Sub

Sub AppStart (Args() As String)
    Dim sys As JavaObject                          
    sys.InitializeStatic("java.lang.System")
    reader.Initialize(sys.GetField("in"))
    
    
    Dim input As String
    Do While(input.ToLowerCase <> "q")
        Log("> ")       ' <==  I would like to print without CRLF here
        input = ReadLine 
        Log($"You wrote '${input}'"$)
    Loop
    Log("Goodbye!!!")
End Sub
 
Upvote 0

teddybear

Well-Known Member
Licensed User
Add a sub print(">") to call native method,
B4X:
Sub print( s As String)
    Private NativeMe As JavaObject
    NativeMe = Me
    NativeMe.RunMethod("print", Array(s))
End Sub
#If JAVA
public static void print(String s) {
    System.out.print(s);
}
#End If
It will not output CRLF
 
Upvote 1

aeric

Expert
Licensed User
Longtime User
B4X:
Sub Process_Globals
    Private reader As TextReader
End Sub

Sub AppStart (Args() As String)
    Dim System As JavaObject
    System.InitializeStatic("java.lang.System")
    reader.Initialize(System.GetField("in"))
    
    PrintLn("Enter 'q' to end input")
    
    Dim input As String
    Do While(input.ToLowerCase <> "q")
        'Log("> ")       ' <==  I would like to print without CRLF here
        Print("> ")
        input = ReadLine
        PrintLn($"You wrote '${input}'"$)
    Loop
    PrintLn("Goodbye!!!")
End Sub

Sub ReadLine As String
    Return reader.ReadLine
End Sub

Public Sub Print (Prompt As String)
    Private nativeMe As JavaObject = Me
    nativeMe.RunMethod("javaPrint", Array(Prompt))
End Sub

Public Sub PrintLn (Prompt As String)
    Private nativeMe As JavaObject = Me
    nativeMe.RunMethod("javaPrintln", Array(Prompt))
End Sub

#If JAVA
public static void javaPrint (String prompt)
{
    System.out.print(prompt);
}

public static void javaPrintln (String prompt)
{
    System.out.println(prompt);
}
#End If
 
Upvote 1
Solution

teddybear

Well-Known Member
Licensed User
If put Log just after this print, it will output ">>>~l0865538:log" in IDE Logs. However, no problem in Windows console.
B4X:
    print(">>>")
    Log("log")
You are right, the message output by Log contains prefix(color, line) ~l0865538 and message "log", in UI Logs window it is shown as a right arrow + message.
 
Upvote 0

chikega

Member
Licensed User
I'm looking over all the code and trying to digest everything. Whatever happened to the good ole Basic input prompt? ;)
FreeBasic code:
FreeBasic:
Dim user_input as string 
Print "Press <q> to exit.."
do
    Input  "> ", user_input
    Print "You entered '" + user_input + "'"   
Loop Until LCase(user_input) = "q"
Print "Goodbye"
Sleep(2000)
End
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
B4X:
Sub AppStart (Args() As String)
    Dim user_input As String
    Print("Press <q> to exit..")
    Do Until user_input.ToLowerCase = "q"
        user_input = Input("> ")
        Print("You entered '" & user_input & "'")
    Loop
    Print("Goodbye")
    Quit
    StartMessageLoop
End Sub

Sub Quit
    Sleep(2000)
    StopMessageLoop
End Sub

Sub Input (Prompt As String) As String
    Dim sys As JavaObject
    sys.InitializeStatic("java.lang.System")
    Print(Prompt)
    Private reader As TextReader
    reader.Initialize(sys.GetField("in"))
    Return reader.ReadLine
End Sub

Sub Print (Message As String)
    Private jo As JavaObject = Me
    jo.RunMethod("print", Array(Message))
End Sub

#If JAVA
public static void print (String message) {
    System.out.print(message);
}
#End If
 
Upvote 0

chikega

Member
Licensed User
Thank you everyone and especially Aeric for digging in and finding a complete solution. I can see more clearly now that B4X is a RAD tool geared for GUI-based solutions. There seems to be quite a bit of "hand-rolling" code in B4X to write a simple interactive console-based application... phew! 🥵 And honestly, at a beginner to intermediate level, I don't fully understand some of the code, but I can copy and paste :)
By the way, Aeric, I combined your above solutions into one solution below which incorporates the sleep routine and input prompt. It works nicely and as expected.

B4X:
Sub Process_Globals
    Private reader As TextReader
End Sub

Sub AppStart (Args() As String)
    Dim user_input As String
    PrintLn("Press <q> to exit..")
    Do Until user_input.ToLowerCase = "q"
        user_input = Input("> ")
        PrintLn("You entered '" & user_input & "'")
    Loop
    Print("Goodbye!!")
    Quit
    StartMessageLoop
End Sub

Sub Quit
    Sleep(2000) '2 second delay
    StopMessageLoop
End Sub

Sub Input (Prompt As String) As String
    Dim sys As JavaObject
    sys.InitializeStatic("java.lang.System")
    Print(Prompt)
    Private reader As TextReader
    reader.Initialize(sys.GetField("in"))
    Return reader.ReadLine
End Sub

Sub Print (Message As String)
    Private jo As JavaObject = Me
    jo.RunMethod("print", Array(Message))
End Sub

Public Sub PrintLn (Prompt As String)
    Private nativeMe As JavaObject = Me
    nativeMe.RunMethod("javaPrintln", Array(Prompt))
End Sub

#If JAVA
public static void print (String message) {
    System.out.print(message);
}

public static void javaPrintln (String prompt)
{
    System.out.println(prompt);
}
#End If

The B4X code above essentially works like this very terse code in FreeBasic, QB64 or cousins:
B4X:
Dim user_input as string  
Print "Press <q> to exit.."
do 
    Input  "> ", user_input
    Print "You entered '" + user_input + "'"    
Loop Until LCase$(user_input) = "q"    
Print "Goodbye"
Sleep(2000)
End

and like this Java code:
Java:
package huw_game_loop;

import java.util.Scanner;

public class huwGameLoop {
    public static Scanner input = new Scanner(System.in);
    public static void main(String[] args) throws InterruptedException
    {
        var userInput = "";
        do
        {
            System.out.print("> ");
            userInput = input.nextLine();
            System.out.println("You wrote '" +userInput+ "'");   

        } while(!userInput.equalsIgnoreCase("q"));  
        
        System.out.println("Goodbye!");   
        Thread.sleep(2000);
    }
}

or even this Object Pascal derivative that runs on .NET.
C:
//Of course, this is not C :->

program IOConsoleLoop1;
var userInput : String;
begin
  Writeln('Press <q> to exit..');
  repeat   
    begin                         
      write('> ');
      userInput := ReadString;
      writeln($'You wrote ''{userInput}''');
    end;
  until userInput.ToLower = 'q';
 Writeln('Goodbye!');
 Sleep(2000);
end.

Thanks again everyone! :)
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
B4X:
Sub Quit
    Sleep(2000) '2 second delay
    StopMessageLoop
End Sub
You can also write a Delay sub like this:
B4X:
Sub Delay (Milliseconds As Int)
    Sleep(Milliseconds)
    StopMessageLoop
End Sub
Then you can call like this:
B4X:
Log("Goodbye")
Delay(2000)
StartMessageLoop

public static Scanner input = new Scanner(System.in);
If you want to use the Scanner class then instead of writing the following:
B4X:
Private reader As TextReader
reader.Initialize(sys.GetField("in"))
Return reader.ReadLine
You can write as:
B4X:
Private scanner As JavaObject
scanner.InitializeNewInstance("java.util.Scanner", Array As Object(sys.GetField("in")))
Return scanner.RunMethod("nextLine", Null)

I put the code in my github: https://github.com/pyhoon/console-read-input-b4j

Thanks for the coffee @chikega
 
Upvote 0

Mashiane

Expert
Licensed User
Longtime User
B4X:
Sub Process_Globals
    Private reader As TextReader
End Sub

Sub AppStart (Args() As String)
    Dim System As JavaObject
    System.InitializeStatic("java.lang.System")
    reader.Initialize(System.GetField("in"))
   
    PrintLn("Enter 'q' to end input")
   
    Dim input As String
    Do While(input.ToLowerCase <> "q")
        'Log("> ")       ' <==  I would like to print without CRLF here
        Print("> ")
        input = ReadLine
        PrintLn($"You wrote '${input}'"$)
    Loop
    PrintLn("Goodbye!!!")
End Sub

Sub ReadLine As String
    Return reader.ReadLine
End Sub

Public Sub Print (Prompt As String)
    Private nativeMe As JavaObject = Me
    nativeMe.RunMethod("javaPrint", Array(Prompt))
End Sub

Public Sub PrintLn (Prompt As String)
    Private nativeMe As JavaObject = Me
    nativeMe.RunMethod("javaPrintln", Array(Prompt))
End Sub

#If JAVA
public static void javaPrint (String prompt)
{
    System.out.print(prompt);
}

public static void javaPrintln (String prompt)
{
    System.out.println(prompt);
}
#End If
This is so awesome. Thank you so much. My CLI can now be enhanced to a console based app. I'm so mind blown, thanks so much Aeric.
 
Upvote 0

AnandGupta

Expert
Licensed User
Longtime User
B4X:
Sub Process_Globals
    Private reader As TextReader
End Sub

Sub AppStart (Args() As String)
    Dim System As JavaObject
    System.InitializeStatic("java.lang.System")
    reader.Initialize(System.GetField("in"))
   
    PrintLn("Enter 'q' to end input")
   
    Dim input As String
    Do While(input.ToLowerCase <> "q")
        'Log("> ")       ' <==  I would like to print without CRLF here
        Print("> ")
        input = ReadLine
        PrintLn($"You wrote '${input}'"$)
    Loop
    PrintLn("Goodbye!!!")
End Sub

Sub ReadLine As String
    Return reader.ReadLine
End Sub

Public Sub Print (Prompt As String)
    Private nativeMe As JavaObject = Me
    nativeMe.RunMethod("javaPrint", Array(Prompt))
End Sub

Public Sub PrintLn (Prompt As String)
    Private nativeMe As JavaObject = Me
    nativeMe.RunMethod("javaPrintln", Array(Prompt))
End Sub

#If JAVA
public static void javaPrint (String prompt)
{
    System.out.print(prompt);
}

public static void javaPrintln (String prompt)
{
    System.out.println(prompt);
}
#End If
Thanks @aeric

The code is so complete and simple, but shows the power of B4X. It is best for beginners to understand.
 
Upvote 0
Top