Post Reply 
Emu48 Edit a CSV File
12-30-2021, 07:24 AM
Post: #1
Emu48 Edit a CSV File
Program: STORE
Checksum: # F2D8h
Size: 283 bytes
Purpose: Store a CSV file to a list after removing the line feed characters. The CSV file is a string.
Problem: The DO loop terminates when no more line feed characters are present in a substring. At this point position would be equal to zero. As expected, the last substring is not removed. I tried an IFTE structure to deal with this, however the program does not execute the false statement. The false statement was supposed to remove the last substring starting after the last line feed character.

On the Emu48: Edit → Load Object... → C:\File → All Files(*.*)
→ Choose file to open → Store the object in FILE → Execute STORE

FILE

"248,1529945.480,521921.773■
249,1530002.951,521922.245■
251,1530058.926,521921.687■
252,1530114.903,521923.001■
254,1530221.977,521924.059"

PNTS

{ }

STORE

<< FILE DUP SIZE 10
CHR 1 1 { } → file
size char start
position points
<<
DO file start
size SUB DUP char
POS DUP ROT 1 ROT
SUB points SWAP +
'points' STO DUP
'start' STO+
'position' STO
UNTIL position
0 ==
END points
'PNTS' STO
>>
>>

@ file - a CSV file stored in FILE
@ size - the SIZE of the CSV file
@ char - the line feed character, character 10
@ start - the start of the next substring after position
@ position - the position of a line feed character in file
@ points - a list of substrings

Any help with this program would be greatly appreciated!
Find all posts by this user
Quote this message in a reply
12-30-2021, 04:34 PM
Post: #2
RE: Emu48 Edit a CSV File
Edit

<< FILE DUP SIZE 10
CHR 1 1 { } → file
size char start
position points
<<
DO file start
size SUB DUP char
POS DUP ROT 1 ROT 2
- SUB points SWAP + @ subtract 2 to exclude line feed character
'points' STO DUP
'start' STO+
'position' STO
UNTIL position
0 ==
END points
'PNTS' STO
>>
>>

@ Resulting list of strings. Note that the last string is empty.

{
"248,1529945.480,521921.773"
"249,1530002.951,521922.245"
"251,1530058.926,521921.687"
"252,1530114.903,521923.001"
"" }
Find all posts by this user
Quote this message in a reply
12-30-2021, 10:30 PM
Post: #3
RE: Emu48 Edit a CSV File
Edit

Program: STORE
Checksum: # C5C6h
Size: 393
Purpose: Removes the line feed characters from a CSV string, and stores individual strings in the form of point number, northing, and easting into a list named PNTS.
Problems: None! The programs works as expected.

<< { } 'PNTS' STO
FILE DUP SIZE 10
CHR 1 1
<< SUB PNTS SWAP +
'PNTS' STO
>> → file size
char start position
Store
<<
DO file start
size SUB DUP char
POS DUP 0 ==
<< file start
size Store EVAL 0
'position' STO 2
DROPN
>>
<< DUP ROT 1
ROT 2 - Store EVAL
DUP 'start' STO+
'position' STO
>> IFTE
UNTIL position
0 ==
END
>>
>>

@ file - local variable, a CSV file stored in FILE
@ size - local variable, the SIZE of the CSV file
@ char - local variable, the line feed character, character 10
@ start - local variable, the start of the next substring after position
@ position - local variable, the position of a line feed character in file
@ Store - subroutine, stores resultant strings

@ Resulting list of strings.

{
"248,1529945.480,521921.773"
"249,1530002.951,521922.245"
"251,1530058.926,521921.687"
"252,1530114.903,521923.001"
"254,1530221.977,521924.059"
}
Find all posts by this user
Quote this message in a reply
12-31-2021, 10:54 AM (This post was last modified: 12-31-2021 11:48 AM by DavidM.)
Post: #4
RE: Emu48 Edit a CSV File
(12-30-2021 07:24 AM)MNH Wrote:  ... Any help with this program would be greatly appreciated!

One approach I've used in the past to do similar things involves building a syntactically-correct string for the final object, then simply converting that string to an object with STR→. SREPL is very handy for converting the linefeeds (it's also very fast).

Here's one way to do that with the sample data you provided. It leaves the result in stack level 1 upon completion:
Code:
\<<
   FILE              @ recall source string to stack
   "{\"" SWAP +      @ place {" chars at beginning
   "\"}" +           @ place "} chars at end
   10 CHR            @ place linefeed char on stack
   "\" \""           @ place " " chars on stack
   SREPL DROP        @ replace all linefeeds with " "
   STR\->            @ convert the resulting string to an RPL object
\>>

This is simply an alternative method for accomplishing the same goal. Hope this gives you some ideas!

(edit: corrected a transcription error in the source code)
Find all posts by this user
Quote this message in a reply
12-31-2021, 06:11 PM (This post was last modified: 12-31-2021 06:14 PM by MNH.)
Post: #5
RE: Emu48 Edit a CSV File
(12-31-2021 10:54 AM)DavidM Wrote:  One approach I've used in the past to do similar things involves building a syntactically-correct string for the final object, then simply converting that string to an object with STR→. SREPL is very handy for converting the linefeeds (it's also very fast).

Thank you, David! Unfortunately, the SREPL command is not available in the HP 48GX command set. I looked briefly in a Donnelly book, but I couldn't find a System-RPL command for SREPL. Do you know of one?
Find all posts by this user
Quote this message in a reply
12-31-2021, 06:51 PM
Post: #6
RE: Emu48 Edit a CSV File
Unfortunately SREPL uses the Saturn+ instructions on the ARM-based calculators. It should be possible to write an equivalent program in standard Saturn code but it wouldn't be as fast.
Find all posts by this user
Quote this message in a reply
12-31-2021, 07:58 PM
Post: #7
RE: Emu48 Edit a CSV File
(12-31-2021 06:51 PM)John Keith Wrote:  Unfortunately SREPL uses the Saturn+ instructions on the ARM-based calculators. It should be possible to write an equivalent program in standard Saturn code but it wouldn't be as fast.

I just found the REPL command in the HP 48 AUR. It looks similar to the SREPL command.
Find all posts by this user
Quote this message in a reply
01-01-2022, 04:41 AM
Post: #8
RE: Emu48 Edit a CSV File
(12-31-2021 10:54 AM)DavidM Wrote:  
(12-30-2021 07:24 AM)MNH Wrote:  ... Any help with this program would be greatly appreciated!

One approach I've used in the past to do similar things involves building a syntactically-correct string for the final object, then simply converting that string to an object with STR→. SREPL is very handy for converting the linefeeds (it's also very fast).

Here's one way to do that with the sample data you provided. It leaves the result in stack level 1 upon completion:
Code:
\<<
   FILE              @ recall source string to stack
   "{\"" SWAP +      @ place {" chars at beginning
   "\"}" +           @ place "} chars at end
   10 CHR            @ place linefeed char on stack
   "\" \""           @ place " " chars on stack
   SREPL DROP        @ replace all linefeeds with " "
   STR\->            @ convert the resulting string to an RPL object
\>>

This is simply an alternative method for accomplishing the same goal. Hope this gives you some ideas!

(edit: corrected a transcription error in the source code)

I tried using REPL to replace characters in a string with white space. No luck.
Find all posts by this user
Quote this message in a reply
01-04-2022, 03:48 PM
Post: #9
RE: Emu48 Edit a CSV File
(12-31-2021 06:11 PM)MNH Wrote:  Thank you, David! Unfortunately, the SREPL command is not available in the HP 48GX command set. I looked briefly in a Donnelly book, but I couldn't find a System-RPL command for SREPL. Do you know of one?

I'm sorry I didn't realize that you were needing to use this on a 48-series system (Emu48 can be used for 49-50 emulation as well). I should have determined that first before posting my response.

I've not seen any built-in SREPL equivalents for the older systems - the code for it is largely a self-contained Saturn block, but a quick look shows that there's at least one branch to an undocumented firmware routine and would likely require some effort to re-code into a GC-safe relocatable code object suitable for the 48GX. Essentially, it would probably be easier just to do what you've already done: code a similar process using UserRPL instead.

The following could also be used as a semi-replacement for SREPL on the 48gx:
Code:
REPLS
\<<
   OVER SIZE                     @ get length of target string
   \-> targ new len              @ assign locals
   \<<
      ""                         @ initial result: empty string
      WHILE
         OVER targ POS DUP       @ loop while target found in remaining source
      REPEAT
         ROT DUP2                @ duplicate target pos/remaining source
         1 ROT OVER - SUB        @ extract chars preceding target
         new + SWAP ROT          @ append repl chars and reposition
         len + 1E8 SUB           @ extract remaining chars
         ROT ROT +               @ reposition/append to result
      END
   \>>
   DROP                          @ drop leftover POS result (0)
   SWAP +                        @ append remaining source to result
\>>

I've named it REPLS to differentiate it from SREPL. Note that this version does not provide a count of "replacements" as SREPL does, so the DROP after SREPL in the earlier code should be removed if used with this version.

The arguments for the above code are as follows.

SL3: Source String
SL2: Target for match (text to be replaced)
SL1: Replacement text

I rarely (if ever) have needed the count result. If you wish that to be provided as well, it shouldn't be too difficult to add that to the above code.
Find all posts by this user
Quote this message in a reply
01-04-2022, 11:06 PM
Post: #10
RE: Emu48 Edit a CSV File
(01-01-2022 04:41 AM)MNH Wrote:  Here's one way to do that with the sample data you provided. It leaves the result in stack level 1 upon completion:
Code:
\<<
   FILE              @ recall source string to stack
   "{\"" SWAP +      @ place {" chars at beginning
   "\"}" +           @ place "} chars at end
   10 CHR            @ place linefeed char on stack
   "\" \""           @ place " " chars on stack
   SREPL DROP        @ replace all linefeeds with " "
   STR\->            @ convert the resulting string to an RPL object
\>>

I'm curious about the backslashes in

"{\"" SWAP + @ place {" chars at beginning
"\"}" + @ place "} chars at end
.

Are they akin to the escape sequence used in C programming?
Find all posts by this user
Quote this message in a reply
01-04-2022, 11:32 PM
Post: #11
RE: Emu48 Edit a CSV File
(01-04-2022 03:48 PM)DavidM Wrote:  \<<
OVER SIZE @ get length of target string
\-> targ new len @ assign locals
\<<
"" @ initial result: empty string
WHILE
OVER targ POS DUP @ loop while target found in remaining source
REPEAT
ROT DUP2 @ duplicate target pos/remaining source
1 ROT OVER - SUB @ extract chars preceding target
new + SWAP ROT @ append repl chars and reposition
len + 1E8 SUB @ extract remaining chars
ROT ROT + @ reposition/append to result
END
\>>
DROP @ drop leftover POS result (0)
SWAP + @ append remaining source to result
\>>[/code]

SL3: Source String
SL2: Target for match (text to be replaced)
SL1: Replacement text

It's my understanding that:

SL3 would be my CSV file
SL2 would be the ■ (linefeed) character
SL1 would be " " (whitespace)

\<<
OVER SIZE @ get length of target string
\-> targ new len @ assign locals
\<<


I don't understand your use of OVER. Wouldn't there be only one object on the stack? Also, you declare 3 local variables, but there would only be 2 objects on the stack.

Thanks again for your help!
Find all posts by this user
Quote this message in a reply
01-04-2022, 11:39 PM
Post: #12
RE: Emu48 Edit a CSV File
(12-31-2021 06:51 PM)John Keith Wrote:  Unfortunately SREPL uses the Saturn+ instructions on the ARM-based calculators. It should be possible to write an equivalent program in standard Saturn code but it wouldn't be as fast.

Is the Emu48 running on a PC, or Android device, using the host's hardware (CPU) or the Emu48's emulated (?) ARM CPU?
Find all posts by this user
Quote this message in a reply
01-05-2022, 12:14 PM
Post: #13
RE: Emu48 Edit a CSV File
(01-04-2022 11:32 PM)MNH Wrote:  I'm curious about the backslashes...
...
I don't understand your use of OVER. Wouldn't there be only one object on the stack? Also, you declare 3 local variables, but there would only be 2 objects on the stack.

The backslash syntax for escaping special characters works on the 50g, but not on the 48gx. A different entry method can be used on the 48gx (using C$ n). See below for what this looks like.

I believe I caused more confusion than help when I posted the REPLS program. That program was meant to be used as a subroutine that would provide a similar function to SREPL, not as a complete program on its own to give the result you were seeking.

The following is a more complete example of what I meant to suggest. It uses the same basic structure and naming of objects that you originally posted. To get the desired outcome, press the menu key for the STORE program. It assumes that the string to be converted is already stored into an object named "FILE", and that the results are to be stored into an object named "PNTS". REPLS is a subroutine which is called by "STORE", and needs to be present in the same directory (or any parent directory in the 48gx hierarchy).

The C$ (counted string) syntax allows you to specify the count of following characters that are considered to be a single string. It allows a more succint way to enter a double quote (") character than using something like "{" 34 CHR +.

I hope this clarifies things for you. Please keep in mind that this is simply meant as an alternative -- I'm not trying to suggest that this is ultimately the best way to do this. This does, however, give you a function (REPLS) that can be used by other programs for targeted substring replacement applications.

Code:
DIR

FILE
"248,1529945.480,521921.773
249,1530002.951,521922.245
251,1530058.926,521921.687
252,1530114.903,521923.001
254,1530221.977,521924.059"

PNTS
{ }

STORE
\<<
   FILE              @ recall source string to stack
   C$ 2 {" SWAP +    @ place {" chars at beginning
   C$ 2 "} +         @ place "} chars at end
   10 CHR            @ place linefeed char on stack
   C$ 3 " "          @ place " " chars on stack
   REPLS             @ replace all linefeeds with " "
   STR\->            @ convert the resulting string to an RPL object
   'PNTS' STO        @ store the result in PNTS
\>>

REPLS
\<<
   OVER SIZE                     @ get length of target string
   \-> targ new len              @ assign locals
   \<<
      ""                         @ initial result: empty string
      WHILE
         OVER targ POS DUP       @ loop while target found in remaining source
      REPEAT
         ROT DUP2                @ duplicate target pos/remaining source
         1 ROT OVER - SUB        @ extract chars preceding target
         new + SWAP ROT          @ append repl chars and reposition
         len + 1E8 SUB           @ extract remaining chars
         ROT ROT +               @ reposition/append to result
      END
   \>>
   DROP                          @ drop leftover POS result (0)
   SWAP +                        @ append remaining source to result
\>>

END
Find all posts by this user
Quote this message in a reply
01-05-2022, 03:22 PM
Post: #14
RE: Emu48 Edit a CSV File
(01-04-2022 11:39 PM)MNH Wrote:  
(12-31-2021 06:51 PM)John Keith Wrote:  Unfortunately SREPL uses the Saturn+ instructions on the ARM-based calculators. It should be possible to write an equivalent program in standard Saturn code but it wouldn't be as fast.

Is the Emu48 running on a PC, or Android device, using the host's hardware (CPU) or the Emu48's emulated (?) ARM CPU?

This wouldn't necessarily make a difference, because Emu48 is equally incapable of running ARM code on ARM systems (like Android devices) as it is on Intel systems (like Windows PCs). It doesn't support escaping the Saturnator to run ARM code like a real 49g+/50g has.

That said, Emu48 on both Windows (with the Emu48+ variant) and Android does support many of the Saturn+ instructions, just not running ARM code.
Visit this user's website Find all posts by this user
Quote this message in a reply
01-08-2022, 11:22 AM
Post: #15
RE: Emu48 Edit a CSV File
Several minor variations of the STORE routine provided above make it possible to output potentially useful objects from the source string.

Example 1

The following STORE code could be used to create a simple list of all of the numbers:
Code:
\<<
   FILE
   "{" SWAP +
   "}" +
   "," " " REPLS
   STR\->
   'PNTS' STO
\>>
PNTS: { 248 1529945.48 521921.773 249 1530002.951 521922.245 251 1530058.926 521921.687 252 1530114.903 521923.001 254 1530221.977 521924.059 }

Example 2

This version is similar to Example 1, but the numbers are encapsulated into sublists of 3:
Code:
\<<
   FILE
   "{{" SWAP +
   "}}" +
   "," " " REPLS
   10 CHR "} {" REPLS
   STR\->
   'PNTS' STO
\>>
PNTS: { { 248 1529945.48 521921.773 } { 249 1530002.951 521922.245 } { 251 1530058.926 521921.687 } { 252 1530114.903 521923.001 } { 254 1530221.977 521924.059 } }

Example 3

Similar to Example 2, but the index number is dropped from each sublist:
Code:
\<<
   FILE
   "{{" SWAP +
   "}}" +
   "," " " REPLS
   10 CHR "} {" REPLS
   STR\->
   1 \<< TAIL \>> DOLIST
   'PNTS' STO
\>>
PNTS: { { 1529945.48 521921.773 } { 1530002.951 521922.245 } { 1530058.926 521921.687 } { 1530114.903 521923.001 } { 1530221.977 521924.059 } }

Depending on how you need to process the data, some of these variations might enable simpler code for subsequent programs.
Find all posts by this user
Quote this message in a reply
01-08-2022, 04:41 PM
Post: #16
RE: Emu48 Edit a CSV File
(01-08-2022 11:22 AM)DavidM Wrote:  Several minor variations of the STORE routine provided above make it possible to output potentially useful objects from the source string.

Thank you for your persistent help!
Find all posts by this user
Quote this message in a reply
01-08-2022, 07:41 PM (This post was last modified: 01-08-2022 08:03 PM by MNH.)
Post: #17
RE: Emu48 Edit a CSV File
(01-04-2022 03:48 PM)DavidM Wrote:  \-> targ new len @ assign locals

I'm still not understanding your syntax. I understand targ means target or the part of the string, what I call FILE, with the 10 CHR (line feed character). Also, len means length, or the SIZE of FILE. What is new? Is it the replacement for targ?

I can't figure out how to use C$ n. I get 'C$ n' on the stack when I use it. I wrote STORE to remove the line feed (LF) characters from the FILE string. It made sense to:

(1) Find the first occurrence of the LF character
(2) Remove the string preceding the LF character, store it in PNTS
(3) Store the string proceeding the LF character, to be used in subsequent loops
(4) Exit the loop when no more LF characters are found

I ended up:

(1) Storing the position of each LF character
(2) Using that position and the FILE length (SIZE) to remove (SUB) the remaining part of FILE
(3) I added (STO+) subsequent positions to a variable until I reached a point where POS
returned a zero

Please note that all substrings will not be of equal lengths, because some coordinate pairs will have point numbers containing 1 to 5 digits. Also, an actual coordinate file will be in the form of P,N,E,Z,D (e.g., 208,1531217.411,520676.455,98.393,IRC 1/2 LB 6300) where:

P is the point number
N is the northing
E is the easting
Z is the elevation
D is the description
Find all posts by this user
Quote this message in a reply
01-09-2022, 02:34 PM
Post: #18
RE: Emu48 Edit a CSV File
(01-08-2022 07:41 PM)MNH Wrote:  I'm still not understanding your syntax. I understand targ means target or the part of the string, what I call FILE, with the 10 CHR (line feed character). Also, len means length, or the SIZE of FILE. What is new? Is it the replacement for targ?

REPLS is a generic string replacement function, and isn't specific to just this application. It takes three arguments on the stack:

SL3: Source string
SL2: Target string to be replaced
SL1: Replacement text that takes the place of target

All instances of the target string in the source string are replaced by the replacement text through one invocation of the function. The result is left on the stack at the completion of the function.

The locals used by the function are:

targ: the text which will end up being replaced in the source string
new: the replacement text for each target encountered
len: the length of the target text (not the length of the source text)

Note that the source string is not a local, but is instead broken into two parts left on the stack during processing: a "result" string, and the "remaining characters" string.

Consider these examples, which are unrelated to your application but are included here to show how the function works:

"ABCDE" "BCD" "X" REPLS => "AXE"

"123 456 789" " " "" REPLS => "123456789"

"1,222.333,0,99999999,88,123.456" "," 10 CHR REPLS => "1
222.333
0
99999999
88
123.456"


(01-08-2022 07:41 PM)MNH Wrote:  I can't figure out how to use C$ n. I get 'C$ n' on the stack when I use it.

"n" here should either be a number, or "$". If a number, it defines the number of characters that follow which are considered to be the contents of the string being defined (ignoring the separator space that follows the number). "C$ $" simply means that the rest of the current line (up to the next linefeed) is all considered to be the string being defined. The "C$ <number>" does NOT show up in the program after you are done editing. Instead, the actual string that you defined is in its place. The C$ construct is an editing aid, and doesn't actually get saved into stored code.

"C$ <whatever>" is used in this situation to make it easier to create a string object that contains double quotes while editing the program.

Here's the description of C$ from the Advanced Users Guide for the 49-50g (I didn't see it in the AUG for the 48G):
Quote:Enters C$ on the command line to help with the manual entry of a string object. Must be followed by a number indicating the number of characters to include in the strings, or an additional $ to indicate that the rest of the command line is a single string. There must be exactly one separator character after the second $ and before the body of the string. If the declared length is greater than the number of characters actually available, the string is automatically truncated to the correct length.

C$ works on both my 48G and 48GX calculators, as well as EMU48 with a 48GX ROM and skin set. In my situation, all are using Rev. R ROMs -- check your version with the VERSION command to see which version you are running.

(01-08-2022 07:41 PM)MNH Wrote:  I wrote STORE to remove the line feed (LF) characters from the FILE string. It made sense to...

This is essentially the same thing that my suggested code is doing. Rather than embedding all of the code in an application-specific code block, though, it separates the string replacement function into an independent function which can also be used in other contexts/applications.

(01-08-2022 07:41 PM)MNH Wrote:  Please note that all substrings will not be of equal lengths, because some coordinate pairs will have point numbers containing 1 to 5 digits.

This isn't an issue with the code I suggested. It doesn't care about length of the embedded strings.

(01-08-2022 07:41 PM)MNH Wrote:  Also, an actual coordinate file will be in the form of P,N,E,Z,D (e.g., 208,1531217.411,520676.455,98.393,IRC 1/2 LB 6300) where:

P is the point number
N is the northing
E is the easting
Z is the elevation
D is the description

The quantity of numbers defined isn't a problem for my suggested code, but the format of the description could be.

STR→ takes whatever is given to it and tries to interpret it as if you typed it into a command line and pressed the ENTER key. If you type numbers separated by commas into the command line and press ENTER, you will end up with those same numbers being placed on the stack in sequence (which would also be the same as what you would get if the numbers were separated by spaces instead of commas). Everything is fine up to that point. In your example given above, that final description has no enclosing double quotes. So that sequence of characters would be interpreted by STR→ as standard RPL code, which would likely result in an error due to what it contains.

Using the method I suggested would only work with the above example if you already had enclosing double quotes surrounding the text of the description. That would require a different approach in this situation, so the method I described would have to be augmented to handle that kind of input. At that point it would probably be simpler just to use application-specific code that would handle the description appropriately.
Find all posts by this user
Quote this message in a reply
01-09-2022, 07:16 PM (This post was last modified: 10-24-2022 04:05 PM by Giuseppe Donnini.)
Post: #19
RE: Emu48 Edit a CSV File
(01-09-2022 02:34 PM)DavidM Wrote:  "C$ $" simply means that the rest of the current line (up to the next linefeed) is all considered to be the string being defined.

Not true, the C$ $ method uses all of the remaining characters following "C$ $ ", including linefeeds, tabs, spaces, control characters, escape characters, whatever. If you happen to use it while entering a program, it will include everything from that point on, even a closing guillemet. It is therefore only useful for entering a single string object.
Find all posts by this user
Quote this message in a reply
01-09-2022, 08:39 PM (This post was last modified: 01-09-2022 08:43 PM by MNH.)
Post: #20
RE: Emu48 Edit a CSV File
(01-09-2022 02:34 PM)DavidM Wrote:  SL3: Source string
SL2: Target string to be replaced
SL1: Replacement text that takes the place of target

targ: the text which will end up being replaced in the source string
new: the replacement text for each target encountered
len: the length of the target text (not the length of the source text)

The stack arguments, SL1 through SL3, don't correspond with the local variables targ, new, and len.
I think my problem is that I don't know, or are confused by, what should be on the stack to make REPLS work. I executed REPLS, and the program didn't remove the line feed character from the first extracted string. Also, the program ended up in an infinite loop.
Find all posts by this user
Quote this message in a reply
Post Reply 




User(s) browsing this thread: 1 Guest(s)