How to add multiple chars between string? - c
I want to add char in my string like '.', '"', '°'... to make it valid for google map location
The code is:
void loop() {
// print the string when a newline arrives:
if (stringComplete) {
String BB = inputString.substring(0, 6);
//String degree = "°";
//String comma = ;
//String dot = ".";
if (BB == signal) {
String LAT = inputString.substring(7, 17);
int LATperiod = LAT.indexOf('.');
int LATzero = LAT.indexOf('0');
if (LATzero == 0) {
LAT = LAT.substring(1);
}
LAT.setCharAt(2, '°');
LAT.setCharAt(5, '\'');
LAT.remove(5,1);
LAT.setCharAt(8, '.');
LAT.setCharAt(12, 'N');
String LON = inputString.substring(20, 31);
int LONperiod = LON.indexOf('.');
int LONTzero = LON.indexOf('0');
if (LONTzero == 0) {
LON = LON.substring(1);
}
LON.setCharAt(2, '°');
LON.setCharAt(5, '\'');
LON.remove(5,1);
LON.setCharAt(8, '.');
LON.setCharAt(12, 'E');
Serial.println("==LATITUDE==");
Serial.println(LAT);
Serial.println("==LONGITUDE==");
Serial.println(LON);
Serial.println("=========================");
}
Output:
==LATITUDE==
2432.06746
==LONGITUDE==
6713.13658
And I want to add chars in this string like it like:
24°32'06.746N 67°13'13.658E
sscanf + sprintf is your solution here.
First let's get needed parts of inputString:
String LAT = inputString.substring(7, 17);
char deg[3], min[3], sec_main[3], sec_frac[4];
sscanf(LAT.c_str(), "%2s%2s.%2s%s", deg, min, sec_main, sec_frac);
Then, collect all these parsed parts into one string:
char latitude[15];
sprintf(latitude, "%s°%s'%s.%s", deg, min, sec_main, sec_frac);
Related
Mapping string to enum value
In my program, I have an enum that is used for indexing my array members. The reason is that it is much easier for me to understand which parameter I am accessing without knowing its index in the array enum param_enum { AA, AB, AC, AD, PARAM_COUNT }; static int16_t parameters[PARAM_COUNT] = { [AA] = 5, [AB] = 3, [AC] = 4, [AD] = 8, }; I can then access any parameter for example: parameters[AA] = 10; // Update AA parameter to value 10. I will be receiving serial commands such as : "AA:15" When I receive this command, I must determine what parameter I need to modify based on the first 2 characters, then skip the 3rd character( because it is just ":" and I dont care about it) and the remaining characters will show the new value) I wonder if there is any easier way to map the enum to a string My current method: // line holds the string data // cmd_size is the length of string data bool parse_command(char *line, uint16_t cmd_size) { printf("data size = %u \n",cmd_size); char temp_buf[3] = {0}; temp_buf[0] = line[0]; temp_buf[1] = line[1]; printf("temp_buf = %s \n",temp_buf); if (!strcmp("aa", temp_buf)) { printf("aa: detected \n"); char temp_storage[5]; int16_t final_value; for(int i = 3;i<=cmd_size; i++){ temp_storage[i-3]=line[i]; // read data and append to temp bufferfrom the 3rd character till the end of line if(line[i] == 0){ printf("null termination triggered \n"); final_value = strtol(temp_storage,NULL,10); // convert char array to int16_t printf("temp var = %i \n",final_value); } } return true; } } The above method seems to work fine but I do not believe that this is the most appropriate solution for this particular task.
If you don't mind what the actual values of the enumeration constants are, you could define those values to be equivalent to the first two characters of the test string. You can then copy those first two characters into a variable of that enum type, which will then adopt the appropriate enumeration directly. You can define the values using two-character integer literals (like 'BA'). On little-endian systems (such as Windows), the two characters would be in reverse order; for big-endian systems, they would be in direct order. Here's an example little-endian implementation: #include <stdio.h> #include <string.h> enum param_enum { // Reverse byte order from strings for little-endian; keep "as-is" for big-endian... AA = 'AA', AB = 'BA', AC = 'CA', AD = 'DA' }; int main(void) { char test[100]; while (1) { printf("Enter test string (Q to quit): "); if (scanf("%99s", test) != 1 || strcmp(test, "Q") == 0) break; enum param_enum penum; memset(&penum, 0, sizeof(penum)); // To clear any 'upper' bytes memcpy(&penum, test, 2); // Now copy the first 2 byte2 switch (penum) { case AA: printf("Code is AA.\n"); break; case AB: printf("Code is AB.\n"); break; case AC: printf("Code is AC.\n"); break; case AD: printf("Code is AD.\n"); break; default: printf("Unknown code.\n"); break; } } return 0; } If your compiler doesn't support multicharacter literals (such support is optional, according to the C Standard, IIRC), you can specify equivalent values using hexadecimal constants and the characters' ASCII codes (assuming your platforms uses ASCII encoding), instead: enum param_enum { AA = 0x4141, // 'AA' AB = 0x4241, // 'BA' AC = 0x4341, // 'CA' AD = 0x4441 // 'DA' };
You could use X Macro technique. #define PARAM_XMACRO \ X(AA) \ X(AB) \ X(AC) \ X(AD) enum param_enum{ #define X(NAME) NAME, PARAM_XMACRO #undef X }; int process() { ... char *val = (char*)param->write.value; #define X(NAME) \ if (strcmp(val, #NAME ":") == 0) { \ printf(#NAME " parameter need to change\n"); \ return NAME; \ } PARAM_XMACRO #undef X return -1; } It will expand as: (newlines added for clarity) enum param_enum{ AA, AB, AC, AD, }; int process() { ... char *val = (char*)param->write.value; if (strcmp(val, "AA" ":") == 0) { printf("AA" " parameter need to change\n"); return AA; } if (strcmp(val, "AB" ":") == 0) { printf("AB" " parameter need to change\n"); return AB; } if (strcmp(val, "AC" ":") == 0) { printf("AC" " parameter need to change\n"); return AC; } if (strcmp(val, "AD" ":") == 0) { printf("AD" " parameter need to change\n"); return AD; } return -1; }
You could use a look-up table of strings indexed by the param enum, and a function to look up the param enum from the string: enum param_enum { AA, AB, AC, AD }; static const char * const param_prefix[] = { [AA] = "AA", [AB] = "AB", [AC] = "AC", [AD] = "AD", }; #define ARRAYLEN(a) (sizeof (a) / sizeof (a)[0]) int find_param(const char *value, size_t value_len) { int i; const char *colon = memchr(value, ':', value_len); if (!colon) { /* not found */ return -1; } /* use length up to colon */ value_len = colon - value; for (i = 0; i < ARRAYLEN(param_prefix); i++) { if (param_prefix[i]) { size_t prefix_len = strlen(param_prefix[i]); if (value_len == prefix_len && memcmp(param_prefix[i], value, prefix_len) == 0) { /* found */ return i; } } } /* not found */ return -1; } Example usage: // (using 3 for length here, but should use something better) int penum = find_param((char*)param->write.value, 3); if(penum >= 0) { printf("%s parameter need to change\n", param_prefix[penum]); }
My string has hidden characters and is longer than what I can visually see?
Problem I have a function which assigns data from reading a Bluetooth connection to a char array QualArray[4][30], although it appears that the length of each char array is 1 char greater than the number of characters that visually appear in the array, suggesting a hidden character? Furthermore, I add these strings to another string called 'resource' using strcpy(), and the length of the string resource is 5 chars more than what it should be (so the 4 additional hidden characters from QualArray makes sense but there is apparently another hidden char added??) I have printed a "_" at the start and end of the string to see if there are any empty chars at either side but there aren't. Somehow there are hidden chars in my strings but I don't know why, any help would greatly be appreciated! Code char resource[160] = "/macros/s/AKfycbzTAkuhTqJfi-EofdpOjBxKNlDe18fdTkOPHMOuOwvl9zbDqfPHwbb1/exec?"; //must be pre-defined buffer char staticresource[160] = "/macros/s/AKfycbzTAkuhTqJfi-EofdpOjBxKNlDe18fdTkOPHMOuOwvl9zbDqfPHwbb1/exec?Cooker=RiceCooker&Meal=Rice&Portion=3&Method=Boil"; char QualArray[5][30]; // [Qparam][charnum] char CookerQuery[20] = "&Cooker="; char MealQuery[15] = "&Meal="; char PortionQuery[15] = "&Portion="; char MethodQuery[15] = "&Method="; void CollectQualitativeData() { int Qparam=0; int charnum = 0; while(Qparam < 4) { // While Qualitative data has not been collectedQualDataCollected = 0 if (SerialBT.available()) { char incomingChar = SerialBT.read(); if (QualArray[Qparam] !="" && incomingChar == '\n') { // If the message isnt empty and the recieved char is nothing (ie if at end of word) if (message !="" Serial.print(QualArray[Qparam]); Serial.print("_"); Serial.print(" length = "); Serial.println(strlen(QualArray[Qparam])); Qparam++; charnum = 0; } if (incomingChar != '\n'){ // If the incomingChar is different than \n, concatenate that char character to QualArray. if (charnum <30) { // if greater than maximum specified array val then ignore QualArray[Qparam][charnum] = incomingChar; charnum++; } } else { } } delay(20); } } void setup() { Serial.begin(115200); SerialBT.begin("ESP32test"); //Bluetooth device name //Serial.println("The device started, now you can pair it with bluetooth!"); CollectQualitativeData(); Serial.println("Complete"); Serial.println(QualArray[0]); Serial.println(QualArray[1]); Serial.println(QualArray[2]); Serial.println(QualArray[3]); Serial.println(QualArray[4]); strcat(resource,CookerQuery); // resource + Resource_variables strcat(resource,QualArray[0]); strcat(resource,MealQuery); // resource + Resource_variables strcat(resource,QualArray[1]); strcat(resource,PortionQuery); // resource + Resource_variables strcat(resource,QualArray[2]); strcat(resource,MethodQuery); // resource + Resource_variables strcat(resource,QualArray[3]); Serial.print("resource=_"); Serial.print(resource); Serial.println("_"); Serial.print("staticresource=_"); Serial.print(staticresource); Serial.println("_"); Serial.print("length static="); Serial.println(strlen(staticresource)); Serial.print("length resource="); Serial.println(strlen(resource)); } https://github.com/Conwon99/4th-Year-Project/blob/main/StringLengthIssue Serial Monitor Debugging RiceCooker_ length = 11 Rice_ length = 5 3_ length = 2 Boil_ length = 5 Complete RiceCooker Rice 3 Boil resource=_/macros/s/AKfycbzTAkuhTqJfi-EofdpOjBxKNlDe18fdTkOPHMOuOwvl9zbDqfPHwbb1/exec&Cooker=RiceCooker&Meal=Ric&Portion=3&Method=Boil_ staticresource=_/macros/s/AKfycbzTAkuhTqJfi-EofdpOjBxKNlDe18fdTkOPHMOuOwvl9zbDqfPHwbb1/exec?Cooker=RiceCooker&Meal=Rice&Portion=3&Method=Boil_ length static=125 length resource=130
char[] has null-terminator value \0 and it is counted as the length of the array. Just use std::vector and String. Anyway, your code is uncompilable. I'll rewrite for you: #include <BluetoothSerial.h> #include <vector> const char resource[] PROGMEM = "/macros/s/AKfycbzTAkuhTqJfi-EofdpOjBxKNlDe18fdTkOPHMOuOwvl9zbDqfPHwbb1/exec?"; //must be pre-defined buffer const char staticresource[] PROGMEM = "/macros/s/AKfycbzTAkuhTqJfi-EofdpOjBxKNlDe18fdTkOPHMOuOwvl9zbDqfPHwbb1/exec?Cooker=RiceCooker&Meal=Rice&Portion=3&Method=Boil"; const char cookerQuery[] PROGMEM = "&Cooker="; const char mealQuery[] PROGMEM = "&Meal="; const char portionQuery[] PROGMEM = "&Portion="; const char methodQuery[] PROGMEM = "&Method="; std::vector<String> qualArray; BluetoothSerial SerialBT; void CollectQualitativeData() { while (qualArray.size() < 4 ) { if (SerialBT.available()) { String incomingChar = SerialBT.readStringUntil('\n'); qualArray.push_back(incomingChar); } delay(20); } } void setup() { Serial.begin(115200); SerialBT.begin("ESP32test"); CollectQualitativeData(); Serial.println("Complete"); for (int i = 0; i < qualArray.size(); i++) { Serial.println(qualArray[i]); } String res(resource); res += cookerQuery + qualArray[0]; res += mealQuery + qualArray[1]; res += portionQuery + qualArray[2]; res += methodQuery + qualArray[3]; Serial.println(res); } void loop() {}
Parsing code for GPS NMEA string
i am trying to parse the incoming GPGGA NMEA GPS string using Arduino uno and below code. What i am trying to do is that i am using only GPGGA NMEA string to get the values of Latitude, longitude and altitude.In my below code, i had put certain checks to check if incoming string is GPGGA or not, and then store the further string in a array which can be further parsed suing strtok function and all the 3 GPS coordinates can be easily find out. But i am unable to figure out how to store only GPGGA string and not the further string.I am using a for loop but it isn't working. I am not trying to use any library.I had came across certain existing codes like this. Here is the GPGGA string information link i am trying to have following functionlity i) Check if incoming string is GPGGA ii) If yes, then store the following string upto EOL or upto * (followed by checksum for the array) in a array, array length is variable(i am unable to find out solution for this) iii) Then parse the stored array(this is done, i tried this with a different array) #include <SoftwareSerial.h> SoftwareSerial mySerial(10,11); // 10 RX / 11 TX void setup() { Serial.begin(9600); mySerial.begin(9600); } void loop() { uint8_t x; char gpsdata[65]; if((mySerial.available())) { char c = mySerial.read(); if(c == '$') {char c1 = mySerial.read(); if(c1 == 'G') {char c2 = mySerial.read(); if(c2 == 'P') {char c3 = mySerial.read(); if(c3 == 'G') {char c4 = mySerial.read(); if(c4 == 'G') {char c5 = mySerial.read(); if(c5 == 'A') {for(x=0;x<65;x++) { gpsdata[x]=mySerial.read(); while (gpsdata[x] == '\r' || gpsdata[x] == '\n') { break; } } } else{ Serial.println("Not a GPGGA string"); } } } } } } } Serial.println(gpsdata); } Edit 1: Considering Joachim Pileborg, editing the for loop in the code. I am adding a pic to show the undefined output of the code. Input for the code: $GPGGA,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*76 $GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A $GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70 $GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79 $GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76 $GPRMC,092750.000,A,5321.6802,N,00630.3372,W,0.02,31.66,280511,,,A*43 $GPGGA,092751.000,5321.6802,N,00630.3371,W,1,8,1.03,61.7,M,55.3,M,,*75 $GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A $GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70 $GPGSV,3,2,11,02,39,223,16,13,28,070,17,26,23,252,,04,14,186,15*77 $GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76 $GPRMC,092751.000,A,5321.6802,N,00630.3371,W,0.06,31.66,280511,,,A*45
After a quick check of the linked article on the NMEA 0183 protocol, this jumped out at me: <CR><LF> ends the message. This means, that instead of just read indiscriminately from the serial port, you should be looking for that sequence. If found, you should terminate the string, and break out of the loop. Also, you might want to zero-initialize the data string to begin with, to easily see if there actually is any data in it to print (using e.g. strlen).
You could use some functions from the C library libnmea. Theres functions to split a sentence into values by comma and then parse them.
Offering this as a suggestion in support of what you are doing... Would it not be useful to replace all of the nested if()s in your loop with something like: EDIT added global string to copy myString into once captured char globalString[100];//declare a global sufficiently large to hold you results void loop() { int chars = mySerial.available(); int i; char *myString; if (chars>0) { myString = calloc(chars+1, sizeof(char)); for(i=0;i<chars;i++) { myString[i] = mySerial.read(); //test for EOF if((myString[i] == '\n') ||(myString[i] == '\r')) { //pick this... myString[i]=0;//strip carriage - return line feed(or skip) //OR pick this... (one or the other. i.e.,I do not know the requirements for your string) if(i<chars) { myString[i+1] = mySerial.read() //get remaining '\r' or '\n' myString[i+2]=0;//add null term if necessary } break; } } if(strstr(myString, "GPGGA") == NULL) { Serial.println("Not a GPGGA string"); //EDIT strcpy(globalString, "");//if failed, do not want globalString populated } else { //EDIT strcpy(globalString, myString); } } //free(myString) //somewhere when you are done with it } Now, the return value from mySerial.available() tells you exactly how many bytes to read, you can read the entire buffer, and test for validity all in one.
I have a project that will need to pull the same information out of the same sentence. I got this out of a log file import serial import time ser = serial.Serial(1) ser.read(1) read_val = ("nothing") gpsfile="gpscord.dat" l=0 megabuffer='' def buffThis(s): global megabuffer megabuffer +=s def buffLines(): global megabuffer megalist=megabuffer.splitlines() megabuffer=megalist.pop() return megalist def readcom(): ser.write("ati") time.sleep(3) read_val = ser.read(size=500) lines=read_val.split('\n') for l in lines: if l.startswith("$GPGGA"): if l[:len(l)-3].endswith("*"): outfile=open('gps.dat','w') outfile.write(l.rstrip()) outfile.close() readcom() while 1==1: readcom() answer=raw_input('not looping , CTRL+C to abort') The result is this: gps.dat $GPGGA,225714.656,5021.0474,N,00412.4420,W,0,00,50.0,0.0,M,18.0,M,0.0,0000*5B
Using "malloc" every single time you read a string is an enormous amount of computational overhead. (And didn't see the corresponding free() function call. Without that, you never get that memory back until program termination or system runs out of memory.) Just pick the size of the longest string you will ever need, add 10 to it, and declare that your string array size. Set once and done. There are several C functions for getting substrings out of a string, strtok() using the coma is probably the least overhead. You are on an embedded microcontroller. Keep it small, keep overhead down. :)
#include <stdio.h> #include <string.h> #define GNSS_HEADER_LENGTH 5 #define GNSS_PACKET_START '$' #define GNSS_TOKEN_SEPARATOR ',' #define bool int #define FALSE 0 #define TRUE 1 //To trim a string contains \r\n void str_trim(char *str){ while(*str){ if(*str == '\r' || *str == '\n'){ *str = '\0'; } str++; } } /** * To parse GNSS data by header and the index separated by comma * * $GPGSV,1,1,03,23,39,328,30,18,39,008,27,15,33,035,33,1*5A * $GNRMC,170412.000,V,,,,,,,240322,,,N,V*2D * $GNGGA,170412.000,,,,,0,0,,,M,,M,,*57 * * #data_ptr the pointer points to gps data * #header the header for parsing GPGSV * #repeat_index the header may repeat for many lines * so the header index is for identifying repeated header * #token_index is the index of the parsing data separated by "," * the start is 1 * #result to store the result of the parser input * * #result bool - parsed successfully **/ bool parse_gnss_token(char *data_ptr, char *header, int repeat_index, int token_index, char *result) { bool gnss_parsed_result = FALSE; // To check GNSS data parsing is success bool on_header = FALSE; // For header int header_repeat_counter = 0; int header_char_index = 0; // each char in header index // For counting comma int counted_token_index = 0; // To hold the result character index bool data_found = FALSE; char *result_start = result; char header_found[10]; while (*data_ptr) { // 1. Packet start if (*data_ptr == GNSS_PACKET_START) { on_header = TRUE; header_char_index = 0; // to index each character in header data_found = FALSE; // is data part found data_ptr++; } // 2. For header parsing if (on_header) { if (*data_ptr == GNSS_TOKEN_SEPARATOR || header_char_index >= GNSS_HEADER_LENGTH) { on_header = FALSE; } else { header_found[header_char_index] = *data_ptr; if (header_char_index == GNSS_HEADER_LENGTH - 1) { // Now Header found header_found[header_char_index + 1] = '\0'; on_header = FALSE; if (!strcmp(header, header_found)) { // Some headers may repeat - to identify it set the repeat index if (header_repeat_counter == repeat_index) { //printf("Header: %s\r\n", header_found ); data_found = TRUE; } header_repeat_counter++; } } header_char_index++; } } // 3. data found if (data_found) { // To get the index data separated by comma if (counted_token_index == token_index && *data_ptr != GNSS_TOKEN_SEPARATOR) { // the data to parse *result++ = *data_ptr; gnss_parsed_result = TRUE; } if (*data_ptr == GNSS_TOKEN_SEPARATOR) { // if , counted_token_index++; // The comma counter for index } // Break if the counted_token_index(token_counter) greater than token_index(search_token) if (counted_token_index > token_index) { break; } } // Appending \0 to the end *result = '\0'; // To trim the data if ends with \r or \n str_trim(result_start); // Input data data_ptr++; } return gnss_parsed_result; } int main() { char res[100]; char *nem = "\ $GNRMC,080817.000,A,0852.089246,N,07636.289920,E,0.00,139.61,270322,,,A,V*04\r\n\\r\n\ $GNGGA,080817.000,0852.089246,N,07636.289920,E,1,5,1.41,11.246,M,-93.835,M,,*5E\r\n\ $GNVTG,139.61,T,,M,0.00,N,0.00,K,A*2F\r\n\ $GNGSA,A,3,30,19,17,14,13,,,,,,,,1.72,1.41,0.98,1*0A\r\n\ $GNGSA,A,3,,,,,,,,,,,,,1.72,1.41,0.98,3*02\r\n\ $GNGSA,A,3,,,,,,,,,,,,,1.72,1.41,0.98,6*07\r\n\ $GPGSV,3,1,12,06,64,177,,30,60,138,15,19,51,322,18,17,42,356,27,1*68\r\n\ $GPGSV,3,2,12,14,36,033,17,07,34,142,17,13,32,267,17,02,21,208,,1*6C\r\n\ $GPGSV,3,3,12,15,05,286,,01,05,037,,03,03,083,,20,02,208,,1*6B\r\n\ $GAGSV,1,1,00,7*73\r\n\ $GIGSV,1,1,00,1*7D\r\n\ $GNGLL,0852.089246,N,07636.289920,E,080817.000,A,A*43\r\n\ $PQTMANTENNASTATUS,1,0,1*4F\r\n"; printf("Parsing GNRMC\r\n"); printf("===============\r\n"); for(int i=1;i<=16;i++){ parse_gnss_token(nem, "GNRMC", 0, i, res); printf("Index: %d, Result: %s\r\n", i, res); } printf("Parsing GNVTG (First Parameter)\r\n"); printf("================================"); // GNVTG - Header, 0 - Repeat Index(if header is repeating), 1 - Value Index, parse_gnss_token(nem, "GNVTG", 0, 1, res); printf("\r\nGNVTG: %s\r\n", res); return 0; }
LZW Decompression in C
I have an LZW compressor/decompressor written in C. The initial table consists of ASCII characters and then each now string to be saved into the table consists of a prefix and a character both saved in a list as int. My compression works but my decompression leaves some characters out. The input: <title>Agile</title><body><h1>Agile</h1></body></html> The output I get (notice the missing 'e' and '<'): <title>Agile</title><body><h1>Agil</h1></body>/html> This is the code I use (the relevant part): void expand(int * input, int inputSize) { // int prevcode, currcode int previousCode; int currentCode; int nextCode = 256; // start with the same dictionary of 255 characters dictionaryInit(); // prevcode = read in a code previousCode = input[0]; int pointer = 1; // while (there is still data to read) while (pointer < inputSize) { // currcode = read in a code currentCode = input[pointer++]; if (currentCode >= nextCode) printf("!"); // XXX not yet implemented! currentCode = decode(currentCode); // add a new code to the string table dictionaryAdd(previousCode, currentCode, nextCode++); // prevcode = currcode previousCode = currentCode; } } int decode(int code) { int character; int temp; if (code > 255) { // decode character = dictionaryCharacter(code); temp = decode(dictionaryPrefix(code)); // recursion } else { character = code; // ASCII temp = code; } appendCharacter(character); // save to output return temp; } Can you spot it? I'd be grateful.
Your decode function returns the first character in the string. You need this character in order to add it to the dictionary, but you should not set previousCode to it. So your code should look like: ... firstChar = decode(currentCode); dictionaryAdd(previousCode, firstChar, nextCode++); previousCode = currentCode; ...
Parsing data from txt file in J2ME
Basically I'm creating an indoor navigation system in J2ME. I've put the location details in a .txt file i.e. Locations names and their coordinates. Edges with respective start node and end node as well as the weight (length of the node). I put both details in the same file so users dont have to download multiple files to get their map working (it could become time consuming and seem complex). So what i did is to seperate the deferent details by typing out location Names and coordinates first, After that I seperated that section from the next section which is the edges by drawing a line with multiple underscores. Now the problem I'm having is parsing the different details into seperate arrays by setting up a command (while manually tokenizing the input stream) to check wether the the next token is an underscore. If it is, (in pseudocode terms), move to the next line in the stream, create a new array and fill it up with the next set of details. I found a some explanation/code HERE that does something similar but still parses into one array, although it manually tokenizes the input. Any ideas on what to do? Thanks Text File Explanation The text has the following format... <--1stSection--> /** * Section one has the following format * xCoordinate;yCoordinate;LocationName */ 12;13;New York City 40;12;Washington D.C. ...e.t.c _________________________ <--(underscore divider) <--2ndSection--> /** * Its actually an adjacency list but indirectly provides "edge" details. * Its in this form * StartNode/MainReferencePoint;Endnode1;distance2endNode1;Endnode2;distance2endNode2;...e.t.c */ philadelphia;Washington D.C.;7;New York City;2 New York City;Florida;24;Illinois;71 ...e.t.c
package filereader; import java.io.IOException; import java.io.InputStream; import java.util.Hashtable; import java.util.Vector; public class FileReader { String locationSection; String edgeSection; Vector locations; Vector edges; public FileReader(String fileName) { // read the contents into the string InputStream is = getClass().getResourceAsStream(fileName); StringBuffer sb = new StringBuffer(); int ch; try { while ((ch = is.read()) != -1) { sb.append((char) ch); } } catch (IOException e2) { e2.printStackTrace(); } try { is.close(); } catch (IOException e) { e.printStackTrace(); } String text = sb.toString(); // separate locations and edges String separator = "_________________________"; // read location section, without last end-of-line char int endLocationSection = text.indexOf(separator) - 1; locationSection = text.substring(0, endLocationSection); // read edges section, without end-of-line char after separator int startEdgeSection = endLocationSection + separator.length() + 3; edgeSection = text.substring(startEdgeSection, text.length()); // parse locations and edges locations = getLocationsVector(locationSection); edges = getEdgesVector(edgeSection); } // parse locations section public Vector getLocationsVector(String section) { Vector result = new Vector(); int startLine = 0; int endLine = section.indexOf('\n'); while (endLine != -1) { String line = section.substring(startLine, endLine); result.addElement(parseLocationsLine(line, ';')); startLine = endLine + 1; if (endLine == section.length() - 1) break; endLine = section.indexOf('\n', startLine); // if no new line found, read to the end of string endLine = (-1 == endLine) ? section.length() - 1 : endLine; } return result; } // parse edges section public Vector getEdgesVector(String section) { Vector result = new Vector(); int startLine = 0; int endLine = section.indexOf('\n'); while (endLine != -1) { String line = section.substring(startLine, endLine - 1); result.addElement(parseEdgesLine(line, ';')); startLine = endLine + 1; if (endLine == section.length() + 1) break; endLine = section.indexOf('\n', startLine); // if no new line found, read to the end of string endLine = (-1 == endLine) ? section.length() + 1 : endLine; } return result; } // parse locations line public Hashtable parseLocationsLine(String value, char splitBy) { Hashtable result = new Hashtable(); int xCEnd = value.indexOf(splitBy); int yCEnd = value.indexOf(splitBy, xCEnd + 1); result.put("x", value.substring(0, xCEnd)); result.put("y", value.substring(xCEnd + 1, yCEnd)); result.put("location", value.substring(yCEnd + 1, value.length() - 1)); return result; } // parse edges line public Hashtable parseEdgesLine(String value, char splitBy) { Hashtable result = new Hashtable(); int snEnd = value.indexOf(splitBy); result.put("startnode", value.substring(0, snEnd)); int n = 1; int start = snEnd + 1; int enEnd = value.indexOf(splitBy, snEnd + 1); int dstEnd = value.indexOf(splitBy, enEnd + 1); while (enEnd != -1 && dstEnd != -1) { result.put("endnode" + String.valueOf(n), value.substring(start, enEnd)); result.put("distance" + String.valueOf(n), value.substring( enEnd + 1, dstEnd)); start = dstEnd + 1; enEnd = value.indexOf(splitBy, start); if (dstEnd == value.length()) break; dstEnd = value.indexOf(splitBy, enEnd + 1); // if last endnode-distance pair, read to the end of line dstEnd = (-1 == dstEnd) ? value.length() : dstEnd; n++; } return result; } // getters for locations and edges public Vector getLocations() { return locations; } public Vector getEdges() { return edges; } } and somewhere in application screen: fr = new FileReader("/map.txt"); Vector vct1 = fr.getLocations(); for (int i = 0; i < vct1.size(); i++) { Hashtable location = (Hashtable) vct1.elementAt(i); Enumeration en = location.keys(); String fv = ""; while (en.hasMoreElements()) { String key = (String) en.nextElement(); String value = (String)location.get(key); fv = fv + value + "-"; } this.add(new LabelField(fv)); } Vector vct2 = fr.getEdges(); for (int i = 0; i < vct2.size(); i++) { Hashtable location = (Hashtable) vct2.elementAt(i); Enumeration en = location.keys(); String fv = ""; while (en.hasMoreElements()) { String key = (String) en.nextElement(); String value = (String)location.get(key); fv = fv + value + "-"; } this.add(new LabelField(fv)); } it will be easy to get values from hashtable by keys: (String)location.get("x") (String)location.get("y") (String)location.get("location") (String)edge.get("startnode") (String)edge.get("endnode1") (String)edge.get("distance1") (String)edge.get("endnode2") (String)edge.get("distance2") ...