C Language: Create single string from several different variable types
John Kauffman
Posts: 653
I'm trying to do what must be a common objective: save experimental data to SD in CSV format to use in Excel. But I can't find all needed code in tutorials or help examples or StackOverflow
My code list below is long but that is because of careful comments and long variable names.
Problems are at ???, mainly the string length calculation, how to prepare data, how to append each type of datum to string.
I'm getting several data from each trial.
Data are mix of int, float, char and string.
My approach is to get all values into a string (with rounding, pad and commas) then open SD file and append the single string.
(repeat for each trial)
Examples in Learn C tutorial, from what I see, only save a hard-coded string.Data are mix of int, float, char and string.
My approach is to get all values into a string (with rounding, pad and commas) then open SD file and append the single string.
(repeat for each trial)
My code list below is long but that is because of careful comments and long variable names.
Problems are at ???, mainly the string length calculation, how to prepare data, how to append each type of datum to string.
/* StoreDataStringver22.c Goal: Concatenate experimental data to string as CSV Then append to SD file and transfer to Excel Notes: Data types = int, float, char, string Goal is the string "dataFullRecord" w/commas Data are each fixed length Unsolved issues round/truncate ints and floats to defined length Convert each type to string Concatenate */ #include "simpletools.h" // Include simple tools int main() // Main function { // Vars to hold data for each trial until written int datum1_Int; // 4 digits always (pad as needed) float datum2_Float; // 5 digits always (need pad) +1 for DP? char datum3_Char; // 1 character always char datum4_String[3]; // 3 character always // might need constant for Comma or Return // String to hold all data of one trial // ??? Size in bytes of total string // int 4 // float 5 +1 for DP? // char 1 // String[3] 3 plus +1 for terminating 0? // Commas 3 // Return added to string or auto by SD append? char dataFullRecord[17]; //no Return character // Get data from experiment - dummy values here [indent] datum1_Int = 12; // round/pad to 4 digits datum2_Float = 9.876543; // round/pad to 5 digits datum3_Char = "A"; // datum4_String[4] = "June"; //always 4 char ?+1 for terminal 0 [/indent] // Build string // how to concatenate? // how to round/pad int & float to fixed length? // how to convert int & float to string? // best way to include commas? // need Return character? // Attempt #6 error: doesn't know function strCopy strCopy(dataFullRecord[](), cstr(round(datum1_int,3)) strCopy(dataFullRecord[](), ",") strCopy(dataFullRecord[](), cstr(round(datum2_float,4)) strCopy(dataFullRecord[](), ",") strCopy(dataFullRecord[](), datum3_char) strCopy(dataFullRecord[](), ",") strCopy(dataFullRecord[](), datum4_string[]) }
Comments
An example using PropWare to do this would like a bit like this:
But if you prefer to stick with what is already available in SimpleIDE and its included libraries, then your best bet is either sprint or fprintf. sprint is smaller since it is part of the "Simple" libraries, but its a 2-step process: create the string, write the string. fprintf will be much larger (because it is part of the C standard library) but easier, because you can write directly to the SD card.
Easier still would be to use sprintf to create the entire record:
Calling strlen() on the result would tell you the length of it.
There is a function called itoa() which will convert from int to ascii, but there's no corresponding ftoa() for floats. For that you'd need to copy an implementation from online somwhere or roll your own. Often it's enough to do something like this:
This won't work if the float it outside of the displayable range of integers - floats can represent very large numbers - but for typical uses it works ok.
Whoops! I don't know why I said "sscan" in my post :P fixing now..
I will switch to sprintf to study this evening as below. It looks like the conversion I was looking for is done easily by the formating codes in the second argument.
To add another value named datum4_string which is a string[3], what would be formater code and syntax of additional argument?
sprintf( dataFullRecord, "%04d,%f,%c", datum1_Int, datum2_Float, datum3_Char );
The number of formatting characters supported varies by implementation, but you're asking for pretty basic stuff here so you'd have access to these anywhere (accept for the special functions that end in "i" provided by Simple, such as printi and sscani). Check out this page for detailed descriptions and examples for the various formatting characters:
http://www.cplusplus.com/reference/cstdio/printf/
And now that you understand they all function the same, I'll show you again the example for PropWare's Printer.printf, which you can use for sprintf as well:
So to convert that to sprintf would be
I did it this way because I had written the convert & write-to-file code for saving ping readings vs angle as I had a robot map a space. I then just copied the snippet for a temperature reading device, and then added a couple of devices that read temperature, pressure, and humidity. As I added data that I wanted to save, I just recopied the snippet.
This is the code I use for saving the T, P, H data. ( I open & close the file after each record since I am taking reading every 10 minutes, and don't want to lose data if the batteries die.)
The numbers I am converting are all floats with 2 decimal places, so I could have used a for() loop, but I have also used that method for chars and ints. If using non floats, I will usually use "sprinti" (with the Simple Libraries) since that saves about 3 to 4k bytes. In this case there was a lot of floating point math, so using the integer/char only formatting function wasn't worth it.
atemp, apres, and ahum are all declared as chars with length of 12, e.g. char atemp[12];
Tom
3 out of 4 data look great when I print the buffer string named dataFullRecord.
(One change I made for the variable datum3_char: Changing the assigned value from "A" to 65.)
But the datum3_string (="Jun") always prints odd characters. Do I have to add the terminating zero to the string I am making?
Here are central lines:
Here is the full code:
Tom
Is that a C thing? I init strings like that all the time - though I generally let the compiler determine the length for me
The difference is that in John Kauffman's code, he is declaring the variable and then assigning to it later, while your are declaring and initializing it at the same time. His code would have worked fine in C++, and yours would have worked fine in C - however, your way is definitely better because it lets the compiler figure out the length.
Now it is on to appending the string to SD, once for each trial.
But why is string length less than observed?
The output is But I count length = 15, including the 2 leading spaces.
In my first experiments writing to the card I only get the whole string saved and read by using stringLength+4
" 12.9.88,A,Jun"
There's no copying of strings going on there, just moving pointers around. a and b are pointers, so what changes is the value they hold, which is an address in this case.
That does sound like a bug, although it's not there in the most recent PropGCC, so it's been fixed since the SimpleIDE release (which was quite some time ago ).
As Jason suggested, strlen() on the string will work.
http://forums.parallax.com/discussion/160431/propgcc-now-in-the-parallax-github/p1