Author Topic: Reading a Text file  (Read 9498 times)

Peter Dulong

  • Newbie
  • *
  • Posts: 31
Reading a Text file
« on: February 18, 2021, 02:35:32 PM »
Is there a way I can have Voice attack read a specific line in a text file?

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #1 on: February 19, 2021, 12:31:58 AM »
You could use native actions for this, but then you'll end up with something like
Code: [Select]
Set text [~t] to [C:\text.txt]
Set integer [~targetLine] value to 6

Begin Integer Compare : [~targetLine] Equals 1
    Set text [~lineOutput] to '{TXTSUBSTR:~t:0:{TXTPOS:"{NEWLINE}":~t:}}'
Else If Text Compare : [{EXP: {INT:~targetLine} = ({TXTOCCURRENCES:"{NEWLINE}":~t} + 1)}] Equals '1'
    Set text [~lineOutput] to '{TXTSUBSTR:~t:{EXP: {TXTLASTPOS:"{NEWLINE}":~t} + {TXTLEN:"{NEWLINE}"}}:}'
Else If Text Compare : [{EXP: {INT:~targetLine} > ({TXTOCCURRENCES:"{NEWLINE}":~t} + 1)}] Equals '1'
    Write [Orange] 'Warning: Target line is {INT:~targetLine}, but the file only contains {EXP:{TXTOCCURRENCES:"{NEWLINE}":~t} + 1} lines' to log
Else
    Set integer [~i] value to 0
    Start Loop : Repeat [{EXP: {INT:~targetLine} - 1}] Times
        Set integer [~i] value to the converted value of {TXTPOS:"{NEWLINE}":~t:{EXP: {INT:~i} + 2}}
    End Loop
    Set text [~lineOutput] to '{TXTSUBSTR:~t:{EXP: {INT:~i} + {TXTLEN:"{NEWLINE}"}}:{EXP: ({TXTPOS:"{NEWLINE}":~t:{EXP: {INT:~i} + 2}} - {INT:~i}) - {TXTLEN:"{NEWLINE}"}}}'
End Condition

Write [Blue] '"{TXT:~lineOutput}"' to log

Which works, but is very nearly unreadable (though to be fair, this could be made less compact for better readability).


As an alternative, I'd go with an inline function that accomplishes the same thing.

The command would look something like
Code: [Select]
Set text [~textFile] to [C:\text.txt]
Set integer [~targetLine] value to 1

Inline C# Function: Get line, wait until execution finishes

Write [Blue] '"{TXT:~lineOutput}"' to log

where "Get line" contains
Code: [Select]
using System;

public class VAInline
{
public void main()
{
string input = VA.GetText("~textFile");
if (input == null)
{
VA.WriteToLog("Cannot get line, no file set", "red");
return;
}

int targetLine = VA.GetInt("~targetLine") ?? -1;
if (targetLine < 1)
{
VA.WriteToLog("Cannot get line, no or invalid line number set", "red");
return;
}

string[] lines = input.Split(new[] {Environment.NewLine}, StringSplitOptions.None);

if (targetLine > lines.Length)
{
VA.WriteToLog("Warning: Target line is " + targetLine + ", but the file only contains " + lines.Length + " lines", "orange");
return;
}

VA.SetText("~lineOutput", lines[targetLine - 1]);
}
}

Peter Dulong

  • Newbie
  • *
  • Posts: 31
Re: Reading a Text file
« Reply #2 on: February 19, 2021, 05:26:08 AM »
Wow, I have so much to learn, I want to be able to open a txt file, Read a certain line from it and have Voice attack say that work or phrase from that line number. I don't know anything about programming. I'm not even Sure where this CODE would go in voice attack....

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #3 on: February 19, 2021, 05:43:28 AM »
Assuming you'll be using the second, more practical example, you'll want to replicate the actions in the second-to-last block, which shows what the action list of your command would look like when all the actions have been added and set up correctly.

There are four actions:

A "Set a Text Value" action, where the "Value from file/URI" option is set to the path of the text file you want to read a line from

A "Set an Integer Value" action, where the "A Value" option is set to the number of the line you want to retrieve

An "Inline Function - C# Code" action, containing the C# code in the last block of my previous post (replace the template code the action starts out with with that), and with its "Wait for the inline function to finish before continuing" option enabled

And lastly, as an example, the final action is a "Write a Value to the Event Log" action, which uses the "{TXT:}" token to get the value of the "~lineOutput" variable which will have been set by the inline function
If you want to have that line read out using text-to-speech, you'd use a "Say Something with Text-To-Speech" action instead (or in addition), using the same token.


All of the actions in the example are found in the "Advanced" submenu of the "Other >" context menu ("Say Something with Text-To-Speech" can be found in the "Sounds" submenu instead)


Press F1 while VoiceAttack has focus to open VoiceAttackHelp.pdf in your default PDF viewer for more information on VoiceAttack's features.

These topics may also be of interest:
Control flow (If, Else, ElseIf, Loop, Jump) basics
Variables and tokens summed up

Peter Dulong

  • Newbie
  • *
  • Posts: 31
Re: Reading a Text file
« Reply #4 on: February 21, 2021, 07:23:53 AM »
Thank you so Much that worked.. And I understand a lot more then I did before. Thank you again, My game will be that much more enjoyable. :D

Peter Dulong

  • Newbie
  • *
  • Posts: 31
Re: Reading a Text file
« Reply #5 on: February 21, 2021, 11:28:09 AM »
Ok I have another question. Lets say in a certain line I only want a certain word from that line. Can I do that,

Example: This is test for line one

I only want the third word in line one. Which is (test)

How can i do that

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #6 on: February 21, 2021, 03:07:03 PM »
Same basic principle, except you'd split on the space character rather than Environment.Newline.

E.G.
Code: [Select]
using System;

public class VAInline
{
public void main()
{
string input = VA.GetText("~textToSplit");
if (input == null)
{
VA.WriteToLog("Cannot get line, no file set", "red");
return;
}

int targetSection= VA.GetInt("~targetSection") ?? -1;
if (targetSection < 0)
{
VA.WriteToLog("Cannot get section, no or invalid section number set", "red");
return;
}

string[] sections = input.Split(new[] {" "}, StringSplitOptions.None);

if (targetSection> sections.Length - 1)
{
VA.WriteToLog("Warning: Target section is " + targetSection + ", but the file only contains " + sections.Length + " sections", "orange");
return;
}

VA.SetText("~sectionOutput", sections[targetSection]);
}
}

To chain the two together more easily, you could have it retrieve "~lineOutput" directly, rather than a separate "~textToSplit" variable.


Alternatively, a hybrid of both could look like:
Code: [Select]
using System;

public class VAInline
{
public void main()
{
string input = VA.GetText("~textFile");
if (input == null)
{
VA.WriteToLog("Cannot get line, no file set", "red");
return;
}

int targetLine = VA.GetInt("~targetLine") ?? -1;
if (targetLine < 1)
{
VA.WriteToLog("Cannot get line, no or invalid line number set", "red");
return;
}

int targetSection = VA.GetInt("~targetSection") ?? -1;

string[] lines = input.Split(new[] {Environment.NewLine}, StringSplitOptions.None);

if (targetLine > lines.Length)
{
VA.WriteToLog("Warning: Target line is " + targetLine + ", but the file only contains " + lines.Length + " lines", "orange");
return;
}

if (targetSection < 0)//If no section has been specified, return the whole line
{
VA.SetText("~splitOutput", lines[targetLine - 1]);
}
else
{
string[] sections = lines[targetLine - 1].Split(' ');

if (targetSection > sections.Length - 1)
{
VA.WriteToLog("Warning: Target section is " + targetSection + ", but the line only contains " + sections.Length + " sections", "orange");
return;
}

VA.SetText("~splitOutput", sections[targetSection]);
}
}
}

In which case you'd set an additional text value, "~targetSection", if you want to get a specific section of a given line (if that value has not been set, or it is less than 0, the entire line will be retrieved).


Importantly, both examples use zero-based section numbers, which is consistent with the "{CMDSEGMENT:}" token, and standard in most programming languages.
This means that, for example, in the phrase "This is test for line one", the word "This" is section 0, whereas section 1 is "is".

Line numbering is still one-based, to be consistent with the line numbers shown in most text editors.


Note that for both examples, the output variable name has also changed to reflect what the expected output may be.

Peter Dulong

  • Newbie
  • *
  • Posts: 31
Re: Reading a Text file
« Reply #7 on: February 22, 2021, 08:46:57 AM »
Ok that worked, But here is the problem I'm having. The file that i need this information from is always changing so the line number maybe different. But there is something in that file that is always Static the phrase "Welcome to" , the next word after that  phrase is what I'm looking for. So how can I do that. ;D

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #8 on: February 22, 2021, 02:18:26 PM »
You could iterate through the "lines" array using a "for" loop (given that you need an indexer to get the line number anyway, I'd use that over a "foreach" loop), or "Array.Find()", to get the line number (which would be zero-indexed, given that arrays are zero-indexed), to which you can add 1 to get the next line.

Peter Dulong

  • Newbie
  • *
  • Posts: 31
Re: Reading a Text file
« Reply #9 on: February 24, 2021, 05:28:09 AM »
I wish I knew more about programming, Thank you for all your help so far. It it muchly Appreciated.

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #10 on: February 24, 2021, 03:49:11 PM »
Reading and trying things for yourself is going to be more effective than wishing.
If you want to be able to use more advanced features, you're going to need to understand them in order to be able to combine them into something that does what you're looking for.


For now, try this command:
Code: [Select]
Set text [~textFile] to [C:\text.txt]
Set text [~startingOffsetSearchTerm] to 'Welcome to'
Set text [~searchMode] to 'StartsWith'
Set integer [~targetLine] value to 1
Set integer [~targetSection] value to -1

Inline C# Function: Get a line and optionally a section of that line, with optional offset based on the contents of a line, wait until execution finishes

Write [Blue] '"{TXT:~splitOutput}"' to log

where "Get a line and optionally a section of that line, with optional offset based on the contents of a line" contains
Code: [Select]
using System;

public class VAInline
{
enum SearchMode
{
Contains,
StartsWith,
EndsWith
}

public void main()
{
string input = VA.GetText("~textFile");
if (input == null)
{
VA.WriteToLog("Cannot get line, no file set", "red");
return;
}

int targetLine = VA.GetInt("~targetLine") ?? -1;
if (targetLine < 1)
{
VA.WriteToLog("Cannot get line, no or invalid line number set", "red");
return;
}

int targetSection = VA.GetInt("~targetSection") ?? -1;

string[] lines = input.Split(new[] {Environment.NewLine}, StringSplitOptions.None);


string startingOffsetSearchTerm = VA.GetText("~startingOffsetSearchTerm");

if (!String.IsNullOrEmpty(startingOffsetSearchTerm))
{
string chosenSearchMode = VA.GetText("~searchMode") ?? "";
SearchMode currentSearchMode = SearchMode.Contains;
if (chosenSearchMode.Equals("StartsWith", StringComparison.OrdinalIgnoreCase))
{
currentSearchMode = SearchMode.StartsWith;
}
else if (chosenSearchMode.Equals("EndsWith", StringComparison.OrdinalIgnoreCase))
{
currentSearchMode = SearchMode.EndsWith;
}

bool searchTermFound = false;
for (int i = 0; i < lines.Length; i++)
{
if (currentSearchMode == SearchMode.StartsWith)
{
if (lines[i].StartsWith(startingOffsetSearchTerm, StringComparison.OrdinalIgnoreCase))
{
searchTermFound = true;
targetLine += i;
break;
}
}
else if (currentSearchMode == SearchMode.EndsWith)
{
if (lines[i].EndsWith(startingOffsetSearchTerm, StringComparison.OrdinalIgnoreCase))
{
searchTermFound = true;
targetLine += i;
break;
}
}
else
{
if (lines[i].IndexOf(startingOffsetSearchTerm, StringComparison.OrdinalIgnoreCase) > -1)
{
searchTermFound = true;
targetLine += i;
break;
}
}
}

if (!searchTermFound)
{
VA.WriteToLog("Search term \"" + startingOffsetSearchTerm + "\" was not found within the file using search mode \"" + chosenSearchMode + "\"; Could not get line", "red");
return;
}
}


if (targetLine > lines.Length)
{
VA.WriteToLog("Warning: Target line is " + targetLine + ", but the file only contains " + lines.Length + " lines", "orange");
return;
}

if (targetSection < 0)//If no section has been specified, return the whole line
{
VA.SetText("~splitOutput", lines[targetLine - 1]);
}
else
{
string[] sections = lines[targetLine - 1].Split(' ');

if (targetSection > sections.Length - 1)
{
VA.WriteToLog("Warning: Target section is " + targetSection + ", but the line only contains " + sections.Length + " sections", "orange");
return;
}

VA.SetText("~splitOutput", sections[targetSection]);
}
}
}

This is again a hybrid of all the preceding functionality, and the new one.

The "~textFile" and "~targetLine" variables are mandatory, I.E. they must have a valid value in order for the inline function to work.

Optional variables are:

"~startingOffsetSearchTerm", which specifies text to search for, so that if that text is found, that line will effectively become line 1 for the purposes of retrieving text (which also means "~targetLine" is relative to that, I.E. setting that to 1 will get the contents of the found line, settings it to 2 will get the contents of the line after the found line, etc...)

"~searchMode", which specifies where within a line the text provided in "~startingOffsetSearchTerm" should be located. Valid values are "StartsWith", "EndsWith", and "Contains", though the latter is the default search mode if nothing (or an invalid value) is specified in "~searchMode".

"~targetSection", which works as it did before (set to -1 in the example, which has the same effect as not setting it at all, I.E. returning the entire line).


Both "~startingOffsetSearchTerm" and "~searchMode" are case-insensitive, E.G. "welcome to" in the former would still allow "Welcome to" to be found, and "startswith" would have the same effect as "StartsWith" in the latter.

If the text specified in "~startingOffsetSearchTerm" cannot be found using the specified search mode, the output variable will not be set.



If you require additional functionality, you'll want to look into C# and string manipulation.



Note that if you need the remainder of the line starting with "Welcome to", but don't want to split it up into individual sections, you could use the "{TXTSUBSTR:}" token on the output with the starting value set to 11, and the length left blank, which will skip the 11 characters (including the space) at the start of the line (it gets the text starting at position 11, which, because as mentioned before most things use zero-indexing, means the twelfth character).

E.G.
Code: [Select]
Write [Blue] '"{TXTSUBSTR:~splitOutput:11:}"' to log

frankly

  • Newbie
  • *
  • Posts: 17
Re: Reading a Text file
« Reply #11 on: June 21, 2021, 07:43:34 PM »
Hello! I'm not native English (am German). I never did any coding and basically just trying to copy things together.

Only added "Say, "{TXT:~lineOutput}" to end of the VoiceAttack-Script itself.

I want to have a single textfile, in which to write, and from which to retrieve text to speech responses. given a option like "positives" "negatives" "jokes" "whatevers". Using Pfeil's code on a textfile (below) I only ever get the response "yes".

Only added
Code: [Select]
"Say, "{TXT:~~splitOutput}" to end of the VoiceAttack-Script itself.

My Textfile Example
Code: [Select]
// positive
yes
positive
roger that
copy that
// negative
no
negative
sorry, cant do that
// jokes
joke 1
joke 2
joke 3
joke 999

My "Pfeil's Value Settings"
Code: [Select]
~startingOffsetSearchTerm "// positive"
~searchMode: "StartsWith"
~targetLine: "2"
~targetSection: "-1" (aka not set)

I assure you, i googled the last two days trying to come up with something on my own.
Tried bunch of "done"-code, kinda worked, tho not as intented. Cried a little.
Then I saw Pfeil's stuff... am now secretly hoping he can work some magic :D

« Last Edit: June 22, 2021, 08:47:04 AM by frankly »

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #12 on: June 21, 2021, 08:30:56 PM »
The "Say Something with Text-To-Speech" action supports semicolon-delimited lists of phrases, from which it will randomly pick one, as well as dynamic response section that allow you to further specify variations within a given phrase.

If you structure your text file in that manner (I.E. all variations for a given response on one line, separated by semicolons), the inline function shown could be used, as it is intended for retrieving a single line.


Otherwise, you could look into splitting the file into lines, and then searching the resulting array for your starting line, iterating through the lines in the array until you find one starting with another header delimiter ("//") or reach the end, and concatenating the lines found into a single semicolon-delimited line
There are other ways as well, but that may be the least fussy, given that you don't directly need to work with character indices and newlines, and you wouldn't need to interact with the file directly.

Copying things together can be a start, but you'll need to gain an understanding of the things you're copying in order to customize them to your own needs, and eventually be able to build things for yourself.
« Last Edit: July 02, 2021, 09:15:21 PM by Pfeil »

frankly

  • Newbie
  • *
  • Posts: 17
Re: Reading a Text file
« Reply #13 on: June 22, 2021, 06:19:06 AM »
uuuh! thank you very much!

and iam slowly getting into all the things because voiceattack is taking over my entire life (and thats good)







frankly

  • Newbie
  • *
  • Posts: 17
Re: Reading a Text file
« Reply #14 on: July 06, 2021, 01:50:45 PM »
I got a list of 89 NAMES, POSITIONS, and DESCRIPTIONS here:
https://docs.google.com/spreadsheets/d/1GCkungVHfiBG0-dfZIubiSINJGh3phTsyPfBkkF64-Y/edit?usp=sharing

Do you have, by any chance, maybe, a simple script to retrieve text and split it in into 3?
I need something simple to start working from because I can't quite get behind all the TXT and TXTPOS, TXTSUBSTRING

And maybe some advice on how to set up "When I say [89 DIFFERENT NAMES]"?


"When I say "Where is NAME" - Say, NAME is at POSITION. DESCRIPTION."

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #15 on: July 06, 2021, 04:54:23 PM »
To retrieve text from a Google spreadsheet? No.

Even if you export that, E.G. as a CSV or TSV file, splitting complex text manually is fairly involved, which is why I generally recommend using an inline function, as I did in this topic.

If you're looking to have a command read out information, the simplest way would be to have your spoken command contain the exact text your text file does, so you can match it directly.
I'd recommend doing this inside the inline function as well, for convenience and efficiency.

Have the inline function split the file into lines (assuming you're using the "Set a Text Value" action's "Value from file/URI" option), find the line starting with the text spoken to trigger the command, then split the line into your three values using the relevant delimiter (comma or tab, if using CSV or TSV, respectively), set variables to those values, then have your command read those out using a "Say Something with Text-To-Speech" action.

DevGnoll

  • Newbie
  • *
  • Posts: 9
Re: Reading a Text file
« Reply #16 on: July 07, 2021, 07:47:10 AM »
I got a list of 89 NAMES, POSITIONS, and DESCRIPTIONS here:
https://docs.google.com/spreadsheets/d/1GCkungVHfiBG0-dfZIubiSINJGh3phTsyPfBkkF64-Y/edit?usp=sharing

Do you have, by any chance, maybe, a simple script to retrieve text and split it in into 3?
I need something simple to start working from because I can't quite get behind all the TXT and TXTPOS, TXTSUBSTRING

And maybe some advice on how to set up "When I say [89 DIFFERENT NAMES]"?


"When I say "Where is NAME" - Say, NAME is at POSITION. DESCRIPTION."

As Pfeil said, export to CSV is your friend.   For the command that is triggered on any one of the words in a column of your spreadsheet, export only that column to CSV, open it in a text editor, replace all the newlines with ;'s  and copy/paste the result into the "when I say" box.   


I happen to be working on a profile that reads in a CSV assumed to have 3 columns (TODO: Fix crash if assumption not valid), filters out empty lines,  and maintains a pointer to a current line.  The cleaned up CSV, number of lines and pointer are stored in the profile.   There are commands to load the CSV,  a command to be added to the appropaite part of startup to get the variables stored in the profile (TODO: test, add "game-appropraite messaging"),   a helper to  read the first 3 columns of  the line addressed by the pointer, and other commands that use the helper to "do something game-appropraite" and advance the pointer (TODO: fix over-protective guardrails).

You could add a command to iterate across all of the lines, use the helper, and compare the value of the first column to the  command prameter, and if it matches "do something game-approparite"   

If that would help, I'll dump it into the profile sharing this evening.   
 

frankly

  • Newbie
  • *
  • Posts: 17
Re: Reading a Text file
« Reply #17 on: July 07, 2021, 08:45:46 AM »
Yea that would help!
I kinda get all the stuff by looking at done scripts and editing them to see what happens. Hit me up! :)


edit: gotta work on my English punctuation. I'm triggering myself... -.-

DevGnoll

  • Newbie
  • *
  • Posts: 9
Re: Reading a Text file
« Reply #18 on: July 07, 2021, 04:39:03 PM »
Here you go.

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #19 on: July 07, 2021, 05:34:00 PM »
A few things that caught my eye, if I may:

Splitting on a newline character is generally not recommended when handling files created on a Windows-based machine.
Windows by default uses a carriage return followed by a newline character as its separator, so when you split on the newline character you're leaving the carriage return in place.
Using the System.Environment.NewLine property would allow you to easily use a non-hardcoded environment-appropriate newline sequence.

Perhaps also worth having a look at the naming conventions for C#/.NET, as underscores are normally only used in special cases.
You're also mixing naming conventions with some variables not having underscores, and some starting with an uppercase character while others don't. VoiceAttack variable names are case-insensitive, but sticking to a single convention is recommended for maintainability, especially when inline functions are involved (as C# variable names are case-sensitive).

Lastly, as a VoiceAttack-specific recommendation, if you have commands that are not intended to be triggered using speech recognition, uncheck the "When I say" option.
The command will still be available for triggering by name using other methods, but will not be passed to the speech recognition engine.



As a more specific example for getting data directly from a target line where manually iterating through the lines is not a requirement:
Code: [Select]
Set text [~tsvFile] to [C:\test.tsv]
Set text [~target] to '{CMDSEGMENT:1}'
Inline C# Function: Find target and set variables, wait until execution finishes
Write [Blue] '"{TXT:~name}" - "{TXT:~position}" - "{TXT:~description}"' to log

Where "Find target and set variables" contains:
Code: [Select]
using System;
using System.Text;
using System.Windows.Forms;

public class VAInline
{
public void main()
{
string input = VA.GetText("~tsvFile");
if (input == null)
{
VA.WriteToLog("Error: \"~tsvFile\" has not been set", "red");
return;
}
string target = VA.GetText("~target");
if (input == null)
{
VA.WriteToLog("Error: \"~target\" has not been set", "red");
return;
}

string[] lines = input.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);

if (VA.Command.Action() == "External")
{
StringBuilder sb = new StringBuilder();

sb.Append("Where is [");

foreach (string line in lines)
{
sb.Append(line.Split('\t')[0]);
sb.Append(";");
}
sb.Remove(sb.Length - 1, 1);
sb.Append("]");

Clipboard.SetText(sb.ToString());
VA.WriteToLog("Command name written to clipboard", "black");
return;
}

foreach (string line in lines)
{
string[] sections = line.Split('\t');

if (sections[0].Equals(target, StringComparison.OrdinalIgnoreCase))
{
VA.SetText("~name", sections[0]);

if (sections.Length > 1)
{
VA.SetText("~position", sections[1]);
}

if (sections.Length > 2)
{
VA.SetText("~description", sections[2]);
}
return;
}
}
VA.WriteToLog("target \"" + target + "\" could not be found");
}
}

The section with the Action() check is completely optional, but I like to do this for commands that rely on a list of data, so that if that data changes, I can right-click the command in the command list, choose "Execute", then edit the command and just paste the clipboard data directly into the "When I say" field, rather than manually modifying it.

Note that this is intended for a TSV file (I.E. using tabs for separation between columns), rather than a CSV file (using commas for separation between columns)
Shouldn't make a difference for your spreadsheet software, which can normally export in either file format, and it should mean not having to deal with double quotes for fields that contain commas.
« Last Edit: July 07, 2021, 10:35:21 PM by Pfeil »

DevGnoll

  • Newbie
  • *
  • Posts: 9
Re: Reading a Text file
« Reply #20 on: July 08, 2021, 08:30:41 AM »
A few things that caught my eye, if I may:



Thanks, I was wondering how to get VA to stop 'recognizing' the two helper commands.

I will take that newline suggestion,  that's my unix csh showing.   I don't think the stray /r would do anything in this instance, but it could have...

As for variable naming conventions,  is there a way to manipulate all of the commands in a profile as text?   Getting variable names consistent through a bunch of search/replaces might make it possible to ditch my cheat-sheet of global and "~~" variables.

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #21 on: July 08, 2021, 08:53:30 AM »
Commands cannot be edited as text directly, however, technically there is a way, though it's not officially supported: You can export the profile as an XML-formatted .vap, which would allow you to find/replace.
Aside from needing to make sure the formatting remains intact, you'll also need to import the commands as commands (rather than as a profile) if you want to keep any data saved to the profile database using the "Save value to profile" option (that data is not exported/imported to/from the .vap file)

Do be sure to make backups, and note that you'll need to deal with any issues arising from manipulating the profile in this manner yourself.

dryh2o

  • Newbie
  • *
  • Posts: 16
Re: Reading a Text file
« Reply #22 on: August 03, 2021, 06:35:33 PM »
@Pfeil - Above, under where you said, "You could use native actions for this, but then you'll end up with something like", do you have that specific command that could be exported to a .vap then, for the lazy, imported as a .vap?

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #23 on: August 03, 2021, 06:39:16 PM »
For the lazy? No.

dryh2o

  • Newbie
  • *
  • Posts: 16
Re: Reading a Text file
« Reply #24 on: August 03, 2021, 07:13:42 PM »
Please disregard. I was able to make the inline C# option work after some trial and error.

FR4NK7Y

  • Newbie
  • *
  • Posts: 11
Re: Reading a Text file
« Reply #25 on: April 16, 2022, 02:15:59 PM »
Sorry to come at you like this again Pfeil, but...

... could you edit the script to do that please? My Skills by now go as far as successfully using state checks on joystick axis in VA for vjoy-mapping.  :'(

Quote
Otherwise, you could look into splitting the file into lines, and then searching the resulting array for your starting line, iterating through the lines in the array until you find one starting with another header delimiter ("//") or reach the end, and concatenating the lines found into a single semicolon-delimited line
There are other ways as well, but that may be the least fussy, given that you don't directly need to work with character indices and newlines, and you wouldn't need to interact with the file directly.

Purpose: only one textfile to edit for all tts-replies, yet massive list of possible replies... (#EPIC)
I promise, I will not ask questions for 3 months then! :D

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #26 on: April 16, 2022, 07:28:34 PM »
The .vap you attached just contains an incorrectly configured copy of the examples I already provided.

If that is your current level of understanding, you're not going to learn much from yet another specific example for a specific use case.


As shown in the examples, text can be split on a delimiter and store in an array, a foreach loop can be used to iterate through the lines, if statements can be used to find the headers, and the StringBuilder class can be used to store and concatenate the contents of several lines.

E.G. with this inline function
Code: [Select]
using System;
using System.Text;

public class VAInline
{
public void main()
{
string input = VA.GetText("~inputFile");
if (input == null)
{
VA.WriteToLog("Error: \"~inputFile\" has not been set", "red");
return;
}

string[] lines = input.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);

string currentHeader = "Unknown";
StringBuilder sb = new StringBuilder();

foreach (string line in lines)
{
if (line.StartsWith("//"))
{
if (sb.Length != 0)
{
sb.Remove(sb.Length - 1, 1);
VA.SetText(">>TTSResponses_" + currentHeader, sb.ToString());
sb.Clear();
}

currentHeader = line.Substring(2).Trim();
}
else
{
sb.Append(line);
sb.Append(";");
}
}

if (sb.Length != 0)
{
sb.Remove(sb.Length - 1, 1);
VA.SetText(">>TTSResponses_" + currentHeader, sb.ToString());
}
}
}

You can have a command like
Code: [Select]
Set text [~inputFile] to [C:\responses.txt]
Inline C# Function: Get TTS responses from file, wait until execution finishes
Write [Blue] '{TXT:>>TTSResponses_Unknown}' to log
Write [Blue] '{TXT:>>TTSResponses_positive}' to log
Write [Blue] '{TXT:>>TTSResponses_negative}' to log
Write [Blue] '{TXT:>>TTSResponses_jokes}' to log

and if "responses.txt" contained
Code: [Select]
something
something else
// positive
yes
positive
roger that
copy that
// negative
no
negative
sorry, cant do that
// jokes
joke 1
joke 2
joke 3
joke 999
the log output would be
Quote
something;something else
yes;positive;roger that;copy that
no;negative;sorry, cant do that
joke 1;joke 2;joke 3;joke 999



This will be the last example on the task of parsing a text file.
Solutions for the majority of use cases can be derived from these examples, with assistance from the Microsoft documentation on .NET
« Last Edit: June 03, 2022, 06:21:38 AM by Pfeil »

FR4NK7Y

  • Newbie
  • *
  • Posts: 11
Re: Reading a Text file
« Reply #27 on: April 16, 2022, 07:38:58 PM »
Thank you very much good sir!

fasu

  • Newbie
  • *
  • Posts: 6
Re: Reading a Text file
« Reply #28 on: June 03, 2022, 05:55:32 AM »
Hello,
Many thanks to @Pfeil for all these C# script templates for VA.

Too bad I came across this web page so late, after spending many hours designing an equivalent command in VA, analyzing text strings character by character (!!!), counting commas, looping through loops with conditions in the conditions !!! But it was fun to do.

Now there's something I can't do, yet it seems so simple. But I can't find my happiness on the internet, despite again several hours of research. It must be said that I am absolutely a total beginner in C# (but I like trying to understand, and from a model, I can sometimes manage).

From a .txt file which is the TXT conversion of a .CSV file, I simply want to find the line number of a given value.

Example :
Anapa,121.00
Batumi;Batoumi,131.00
Beslan,141.00
Gelendzhik,126.00
Gudauta,130.00
Kobuleti,133.00

Point of vigilance: the value of column 0 (0-based) is sometimes a text containing ";"

My objective :
If ~target = "Batoumi" then ~output = 3 (or 2 if 0-based).

It looks so simple written like this, and yet, insurmountable for me ;-(
Do you have an idea ?

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4781
  • RTFM
Re: Reading a Text file
« Reply #29 on: June 03, 2022, 06:30:16 AM »
From a .txt file which is the TXT conversion of a .CSV file, I simply want to find the line number of a given value.
How did you perform this "conversion"? A CSV file is literally a text file with specific formatting and a different extension.


As mentioned previously, examples have now been provided. You can either modify them, or perhaps infer from them which methods would apply to your use case.

E.G. what you are describing could be done by splitting the file on its newline characters into an array, looping through the array with a for loop, and checking whether the element contains (or starts with, if preferred) the value you're looking for, and returning (I.E. storing in a VoiceAttack variable) the value of the iterator of the loop if it does.

If you want to ignore the first line if it consists of a specific value, add a condition ("if") to check whether the first element in the array (at index 0) equals that value, and decrement the iterator by -1 before storing its value.


The Microsoft documentation on .NET contains more information on these language features. Note that inline functions currently use C# 5.0, as released with .NET Framework 4.5, due to changes in later versions of the toolchain.