An appeal on behalf of the hard of thinking.
Hugh
Posts: 362
At this time of year my thoughts turn to those, like myself, who have difficulties understanding zero-terminated strings, pointers and the like. Often un-noticed and unacknowledged by society during the year, such people are afraid to let their predicament be widely know for fear of ridicule, abuse and - quite simply - the shame and stigma of the condition. Without you being aware, one of these people might be a member of your family, a friend or a colleague - hiding their lack of understanding behind a brave face and only letting their condition be visible in the comfort of their own home, or in the company of fellow sufferers.
Can you, in this time of festive cheer, spare a few seconds of your busy time to help such a person? It might take you only moment, but could change the life of a sufferer. No money need change hands.
"MY NAME IS HUGH. I DON'T UNDERSTAND STRINGS AND THINGS" (despite Mike Green's valiant attempts to educate me).
There I have said it. I am 'out' and asking for help....
OK, I am "p-reading" some bytes from FSRW:
to read the contents of a file that contains only "12A" as text.
The above code results in '0312A |o±' being displayed on the LCD so:
- The file opens for read OK
- Three bytes were inputted
- The three bytes of SDBuf were sent as a string to the LCD
I am guessing that the gibberish after the data I read is due to the string not being zero-terminated, etc.
As a simple question: "if a text file contains an ASCII representation of a number (e.g. "1234"), how can I input and process this as a number from an SD card using FSRW?". I am happy raising numbers to a power of ten to give place value, etc., but it is the getting of the value that is the problem.
Help! Please?!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Hugh - the thinking woman's Geoffrey Pyke.
Can you, in this time of festive cheer, spare a few seconds of your busy time to help such a person? It might take you only moment, but could change the life of a sufferer. No money need change hands.
"MY NAME IS HUGH. I DON'T UNDERSTAND STRINGS AND THINGS" (despite Mike Green's valiant attempts to educate me).
There I have said it. I am 'out' and asking for help....
OK, I am "p-reading" some bytes from FSRW:
sdfat.mount (3) r := sdfat.popen(string("TEST.txt"), "r") lcd.dec(r) r := sdfat.pread(SDbuf,3) lcd.dec(r) repeat r lcd.str(SDBuf[noparse][[/noparse]0])
to read the contents of a file that contains only "12A" as text.
The above code results in '0312A |o±' being displayed on the LCD so:
- The file opens for read OK
- Three bytes were inputted
- The three bytes of SDBuf were sent as a string to the LCD
I am guessing that the gibberish after the data I read is due to the string not being zero-terminated, etc.
As a simple question: "if a text file contains an ASCII representation of a number (e.g. "1234"), how can I input and process this as a number from an SD card using FSRW?". I am happy raising numbers to a power of ten to give place value, etc., but it is the getting of the value that is the problem.
Help! Please?!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Hugh - the thinking woman's Geoffrey Pyke.
Comments
A couple us did this "SD Trainer" a while back and if you learn by example, you'll find it useful. [noparse]:)[/noparse]
www.warrantyvoid.us/files/SD_trainer.zip
OBC
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
New to the Propeller?
Visit the: The Propeller Pages @ Warranty Void.
Thank you, Gentlemen!
What I am trying to do is increment a file-name counter, reading the value of the previous filename from a file named "Test.txt". With your help I now have a variable that contains an integer for the file name. For the sake of an example, that value is 12
If I do this:
I get a file named '12' on the SD card. However, being Mr Picky, I'd like to include a file extension - which is where it seems to get tricky!
The intention of the following cod was to take the integer variable, turn it into a (zero-terminated) string and then append a "*.CSV" extension.
It doesn't quite work. Any suggestions?
I am back to logging four parameters from my car ECU. Yesterday it was snowing and the rpm rarely got above 2.000 rpm (see attached!)
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Hugh - the thinking woman's Geoffrey Pyke.
That was funny.....here is my attempt to help
A string can be thought of as a one dimensional array of BYTES. However, since a string can be of a random length it would be wasteful and inflexible to just fix the length of the array. Therefore a TRICK is used to INDICATE the ending of a string. We just use an additional byte and fill it with a value of 0.
·
Thus any actions performed on this one-dimensional array of bytes proceed from the START of the array and progress one byte a time always checking to see if the byte contains a value Zero·or not. If it does then the processing finishes.
·
Now in Spin (as in other languages) a variable can be referred to directly by name but their type (i.e. size) has to be known (e.g. byte, word, long).
·
However, if you need to refer to a collection (i.e. array) of variables then you can do this by referring to the ADDRESS in memory of the first one in the collection. After that you can access the next element and the one after that and so forth by adding an offset to the ADDRESS of the first element in the collection
·
So for instance if we think of memory as slots and we know that each slot holds ONE LETTER then if we know that the sentence we want to access STARTS at slot number 10 then we can access the entire sentence by getting the first letter from slot number 10, then the next one from slot number 10+1 and the third letter from slot number 10+2 etc.
·
So how do we know when the sentence has ended. Well, we check for the slot that contains a NULL letter (i.e. value of 0).
·
By the way this is not ‘0’ it is rather the value 0. Remember that letters have ASCII codes but the Ascii Code 0 is not considered as a·letter rather it is the NULL character.
·
So in order to define a string we need the ADDRESS of the first character of the letter and then make sure that the last character is the NULL (0) character.
·
To provide the address of the first character you can do it in one of two ways.
1-···· Use an actual memory address e.g. $10A7….. but this become untenable unless you have put the string there your self. However, normally the compiler will put the string where it deems suitable and you cannot know the address easily.
2-···· Since the compiler knows where the address is why not let it figure it out.
So we do this by specifying a name to the beginning of the string and then use the @ operator to tell the compiler that it is not the value inside the variable that we want rather the address in memory of the variable.
·
So when we have a variable named MyVariable and we use the name then we are telling the compiler we want the value stored at that variable. If we use the @ operator in front of the name e.g. @MyVariable then in fact we are telling the compiler to use the MEMORY POSITION of the variable not what is in that memory.
·
So then to operate on a string we need to store the string in memory somewhere and give a name to the FIRST byte of that string. Then also we need to make sure that the last byte of the string is assigned a 0.
·
This is usually done in the DAT section e.g.
·
Dat
··· MyString· byte· “this is the body of the string”,0·· ‘notice the last byte is a zero
·
In the above we have told the compiler that the first byte is called MyString. Also we told it that all the stuff that comes afterwards is BYTEs. Then we gave values to the bytes as the characters specified. The compiler knows to convert the characters to their Ascii values and to store these numbers in the consecutive bytes. Then we expressly stored a 0 in the byte that follows all these.
·
Now later when we want to process the string in some form or another. For example in the method of the FullDuplexSerial called Str() we give the method the ADDRESS of the string’s first byte @MyString. The method then knows how to proceed with the string since it now knows the end is also the byte with the 0 value.
·
The above is good if you already have stored the string in a memory at a known position (i.e. in the DAT section). This is also useful and efficient especially if you are going to use the string many times. However, sometimes you need to use a string only once. In this case you can create the string ON THE FLY.
·
You do this by instructing the compiler to create the string for you and put it where it wants in RAM but also to make sure it has a NULL (0) terminator.
·
This is achieved by the String() function in Spin. For example String(“my string”[noparse];)[/noparse]. This function also returns the address where it put the string.
·
So for example to use the FullDuplexSerial object’s Str() method you can do this.
·
Debug.Str(@MyString)·· ‘ using the preset string in the Dat section above
Debug.Str(String(“test string”[noparse];)[/noparse]) ‘using the on the fly method.
·
·
I hope this has been clear enough.
·
Regards
·
Samuel
Thanks also for the help! Been through the "Fundamentals" twice. Always hit the wall trying to keep the address vs content straight in my peble mind. Also remembering that strings are ASCII codes sometimes messes with me. Any how thanks for your help and sorry for interrupting this thread.
Ken
The code you posted would not work.
1- The Dec() method makes a number out of a string not a string out of·number. you need to use the ToStr() method.
2- The variable Fi would point to the to the beginning of the string (assuming you used the ToStr() method not Dec() method)
··· and when you say byte[noparse][[/noparse]@Fi] := "." you are making a byte in memory··become the value "." but it is not the string.
3-So what you need is byte[noparse][[/noparse]Fi] := "."
4- BUT..... that still does not work since you are not APPENDING you are OVERWRITING the string since Fi points to the first
··· character of the string.
5- what you need is byte[noparse][[/noparse]Fi + L] := "." where L is the length of the string. So you need a way of finding the length of the string.
6- for the next one you need to increment by 1, so you say byte[noparse][[/noparse]Fi+L+1] := "C", byte[noparse][[/noparse]Fi+L+2] := "S" etc.
7- L can be determined by going through the string until you encounter a 0 character eg
··· L:=0
··· repeat
······ if byte[noparse][[/noparse]Fi+L] == 0
········· quit
······ L++
7- Use L:= StrSize(Fi) to determine the length of the string.
I hope this helps.
Samuel
Post Edited (SamMishal) : 12/23/2009 10:51:21 AM GMT
It would be useful to know what the types of num, Fi, and fileNum are; please post more of your code (or attach it to your post).
The 'as is' code (before I make any changes based on what I have learned) looks like:
In other words, I want the prop to wake-up and write to a new file each time it is powered, i.e., "1.csv", "2.csv" ... "n.csv", using a file on the SD drive to store the most recently used number.
Yes, the above code is a bit clunky (particularly where it reads the reference from the file and turns it to an integer) but this allowed me to verify each step.
If I understand correctly
ends up with an area of memory that, for fileName = 12, would look like:· 49|50|0
So I need to make·"Fi" look like 49|50|46|67|83|86|0 ("12.CSV") before I can use it as a filename.(?)
Thanks,
Hugh
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Hugh - the thinking woman's Geoffrey Pyke.
Thanks,
Hugh
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Hugh - the thinking woman's Geoffrey Pyke.
What kind of object is num?
You might consider adding a couple of helper functions:
Then you should be able to just say
If it is then num.Dec() is not the right thing to use....???
Any way if it is and num.Dec creates a string out of the FileName, then Fi is now a pointer (i.e. address) to the
first character in the string.
Now assuming that the BUFFER was sized to be longer than the actual string then you need to do this
L := 2
byte[noparse][[/noparse]Fi+L] := "."
byte[noparse][[/noparse]Fi+L+1] := "C"
byte[noparse][[/noparse]Fi+L+2] := "S"
byte[noparse][[/noparse]Fi+L+3] := "V"
byte[noparse][[/noparse]Fi+L+4] := 0
·
·
This would work assuming that you ALWAYS have Filename being no longer than 2 digits. If it can be longer than 2 digits
then you need to change L to be the length of the string created.
(see other posting below)
ALSO....and this is important.....the area in memory where num.Dec() creates the string must have been sized sufficiently so as when you add those 4 characters you are not running over another memory area that may corrupt the program.
Samuel
P.S. NOTICE..... it is byte[noparse][[/noparse]Fi+L]··or byte[noparse][[/noparse]Fi][noparse][[/noparse]L]· BUT· NOT·· byte[noparse][[/noparse]@Fi][noparse][[/noparse]L] or byte[noparse][[/noparse]@Fi+L]· ...... this because the value
in Fi is the address you want and thus you want the value of Fi not its address which is not the address you need.
You need the address of the string created by num.Dec() which is the value returned by the method and is stored
in Fi. So when you need to access the string you use Fi not @Fi since the address you want is now stored in Fi.
@Fi will give us the address of the variable Fi and not the address of the string.
Post Edited (SamMishal) : 12/23/2009 10:49:49 AM GMT
I always find enlightening to have a peek at the bytes produced by a compiler, contained in a file.. in memory. To write them down to paper also helps. There is always an area where one of us is lacking: parsing algebraic expressions and transforming them to polish notation is one area where I am mostly lost. :-(.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Visit some of my articles at Propeller Wiki:
MATH on the propeller propeller.wikispaces.com/MATH
pPropQL: propeller.wikispaces.com/pPropQL
pPropQL020: propeller.wikispaces.com/pPropQL020
OMU for the pPropQL/020 propeller.wikispaces.com/OMU
This function returns the length of a ZString· (i.e. zero terminated string).
So my previous code can now be modified easily to
·· L := StrSize(Fi)
·· byte[noparse][[/noparse]Fi+L] := "."
·· byte[noparse][[/noparse]Fi+L+1] := "C"
·· byte[noparse][[/noparse]Fi+L+2] := "S"
·· byte[noparse][[/noparse]Fi+L+3] := "V"
·· byte[noparse][[/noparse]Fi+L+4] := 0·· 'note you need this to maintain a o terminated string
so disregard the previous suggestion for finding the size of a string. Also there is no need to worry
about the size. Just use the StrSize() function as shown above.
This way you always can start appending the ".CSV" to the end of the string regardless of the
value in FileNum. FileNum can now be any number you want and as long as the num.Dec()
converts it to a string and returns the pointer which you store in Fi then you can use the above
code to append the extension as you need.
Of course you also have to ensure that the buffer where num.Dec() is storing the created string is
long enough to hold the extra 4 bytes without running into code.
SPIN is GREAT ..... the StrSize(), String() and StrComp() are of great help when dealing with strings.
Samuel
Post Edited (SamMishal) : 12/23/2009 10:48:07 AM GMT
(The Num object is "Simple_Numbers".)
Thank you!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Hugh - the thinking woman's Geoffrey Pyke.
Yes then what num.Dec() does is exactly what you need. Also I checked and
the object creates a Buffer of 64 bytes to hold the string so it is GOOD.
Then Just use the modified code above.
EXACTLY...... Give a man a fish and he will eat for one day and teach him to fish he will drown.....
I am glad you understand it..... can you now explain it to me...
No seriously I am GLAD you do understand it. Now you can do WHATEVER you need.
Samuel
You should define the Fi variable as a Long in your Var section not as a Byte Fi[noparse][[/noparse]10]· but rather as a Long Fi.
or
You can remove it from the Var section and have it as a Local variable in Main...like this
Pub Main | Fi
All variables that are to be Pointers....i.e. a variables that·are going to be used to store the Address
of other variables ..... SHOULD be Longs.....you can get by with a WORD instead since the Propeller
has a maximum of 32K RAM and thus·Word would suffice....but definately not a Byte.
If you use it as a local variable it would be long automatically anyway.
Samuel
·
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Hugh - the thinking woman's Geoffrey Pyke.