I have a .txt file loaded into the SD card which contains:
SampleTime: 100
SampleInterval : 1000
Phone: 91987654331
I am reading this using the readStringUntil(':'); in Arduino IDE but it reads the whole content together, but I want to split the string and integer and store it in different variables. I want to know how I can split it easily and store it in different variables. Below is my code:
void setup() {
Serial.begin(9600);
if (SDfound == 0) {
if (!SD.begin(4)) {
Serial.print("The SD card cannot be found");
while(1);
}
}
SDfound = 1;
printFile = SD.open("consta.txt");
if (!printFile) {
Serial.print("The text file cannot be opened");
while(1);
}
while (printFile.available()) {
buffer = printFile.readStringUntil(':');
Serial.println(buffer); //Printing for debugging purpose
//do some action here
}
You could consider parsing each whole line with the sscanf():
See: https://cplusplus.com/reference/cstdio/sscanf/
Here is an example implementation for how to parse your lines using sscanf() (for simplicity, I'm ignoring the SD card, and just providing manual c-string literals):
void setup() {
Serial.begin(9600);
char testline_1[] = "SampleTime: 100";
char testline_2[] = "SampleInterval: 1000";
char testline_3[] = "Phone: 91987654331";
// Display initial inputs
Serial.println("===== Initial Inputs =====");
Serial.println(testline_1);
Serial.println(testline_2);
Serial.println(testline_3);
Serial.println();
// Parse data
int sample_time;
int sample_interval;
char phone_number[20];
sscanf(testline_1, "%*s %i", &sample_time);
sscanf(testline_2, "%*s %i", &sample_interval);
sscanf(testline_3, "%*s %s", phone_number);
// Display parsed data
Serial.println("===== Parsed Data =====");
Serial.print("sample_time = "); Serial.println(sample_time);
Serial.print("sample_interval = "); Serial.println(sample_interval);
Serial.print("phone_number = "); Serial.println(phone_number);
Serial.println();
}
void loop() {
}
This is what is output to the Serial Monitor, demonstrating successful parsing of the input:
===== Initial Inputs =====
SampleTime: 100
SampleInterval: 1000
Phone: 91987654331
===== Parsed Data =====
sample_time = 100
sample_interval = 1000
phone_number = 91987654331
You can use strtok() to split the string:
// let say buffer = "Ahmed:2000:"
// using ':' as split character
char *value = strtok(buffer, ":");
// now value have the first word before ':' ----> Ahmed
value = strtok(NULL, ":");
//now value contain the second word ----> "2000" as string
Then use atoi() to convert from a string to an int:
int number = atoi(value);
Related
I am trying to read each line of a file and store binary values into appropriate variables.
I can see that there are many many other examples of people doing similar things and I have spent two days testing out different approaches that I found but still having difficulties getting my version to work as needed.
I have a txt file with the following format:
in = 00000000000, out = 0000000000000000
in = 00000000001, out = 0000000000001111
in = 00000000010, out = 0000000000110011
......
I'm attempting to use fscanf to consume the unwanted characters "in = ", "," and "out = "
and keep only the characters that represent binary values.
My goal is to store the first column of binary values, the "in" values into one variable
and the second column of binary values, the "out" value into another buffer variable.
I have managed to get fscanf to consume the "in" and "out" characters but I have not been
able to figure out how to get it to consume the "," "=" characters. Additionally, I thought that fscanf should consume the white space but it doesn't appear to be doing that either.
I can't seem to find any comprehensive list of available directives for scanners, other than the generic "%d, %s, %c....." and it seems that I need a more complex combination of directives to filter out the characters that I'm trying to ignore than I know how to format.
I could use some help with figuring this out. I would appreciate any guidance you could
provide to help me understand how to properly filter out "in = " and ", out = " and how to store
the two columns of binary characters into two separate variables.
Here is the code I am working with at the moment. I have tried other iterations of this code using fgetc() in combination with fscanf() without success.
int main()
{
FILE * f = fopen("hamming_demo.txt","r");
char buffer[100];
rewind(f);
while((fscanf(f, "%s", buffer)) != EOF) {
fscanf(f,"%[^a-z]""[^,]", buffer);
printf("%s\n", buffer);
}
printf("\n");
return 0;
}
The outputs from my code appear as follows:
= 00000000000,
= 0000000000000000
= 00000000001,
= 0000000000001111
= 00000000010,
= 0000000000110011
Thank you for your time.
The scanf family function is said to be a poor man'parser because it is not very tolerant to input errors. But if you are sure of the format of the input data it allows for simple code. The only magic here if that a space in the format string will gather all blank characters including new lines or none. Your code could become:
int main()
{
FILE * f = fopen("hamming_demo.txt", "r");
if (NULL == f) { // always test open
perror("Unable to open input file");
return 1;
}
char in[50], out[50]; // directly get in and out
// BEWARE: xscanf returns the number of converted elements and never EOF
while (fscanf(f, " in = %[01], out = %[01]", in, out) == 2) {
printf("%s - %s\n", in, out);
}
printf("\n");
return 0;
}
So basically you want to filter '0' and '1'? In this case fgets and a simple loop will be enough: just count the number of 0's and 1's and null-terminate the string at the end:
#include <stdio.h>
int main(void)
{
char str[50];
char *ptr;
// Replace stdin with your file
while ((ptr = fgets(str, sizeof str, stdin)))
{
int count = 0;
while (*ptr != '\0')
{
if ((*ptr >= '0') && (*ptr <= '1'))
{
str[count++] = *ptr;
}
ptr++;
}
str[count] = '\0';
puts(str);
}
}
Here is response of the AT command I want to read only phone number and text message? How can I code for this?
+CMGR: "REC READ","+18142150657","Joseph cell","11/02/24,19:53:35-20",145,4,0,0,"+13123149621",145,9
Message 1
OK
Here's what they said "Get ready here we come"
+CMGR: "REC READ","+18145806808","Google voice","11/02/25,23:44:56-20",145,4,0,0,"+13123149621",145,46
OK
This is from marys phone
+CMGR: "REC READ","+18140521232","Mary cell","11/02/25,21:34:00-20",145,4,0,0,"+13123149621",145,24
OK
ERROR
I would like to provide alternative solution, solving some issues that are not evaluated in the other answers.
The number has to be stored as a string (since '+' char is part of it)
Parsering an AT command response is not so easy as one could expect, since within double quotes comas are allowed (e.g in the phonebook name we could have something like "Smith, John"
Before including code of my simple parsering solution, let's do some assumptions:
It is an implementation without sscanf(). I think it will be useful in order to show explicit parsing logic
It is an implementation tailored on requirements needs. Since only phone number is relevant for us, we can afford discarding all the remaining part of the main response
Maximum phone number length is 20 (indeed 19+1)
Maximum SMS length is 200 (indeed 199+1)
Here is the code:
#include <stdio.h>
#include <string.h>
#define MAX_NUM_LEN 20
#define MAX_SMS_LEN 200
int main( void )
{
char number[MAX_NUM_LEN];
char text[MAX_SMS_LEN];
char exampleCGMRResponse[] = "+CMGR: \"REC READ\",\"+18142150657\",\"Joseph cell\",\"11/02/24,19:53:35-20\",145,4,0,0,\"+13123149621\",145,9\r\nMessage 1\r\n\r\nOK";
char *pTmp;
memset(number, 0, sizeof(number));
memset(text, 0, sizeof(text));
/* Get phone number */
pTmp = strstr( exampleCGMRResponse, "+CMGR: \"REC READ\",\"" );
if( pTmp )
{
strncpy( number, pTmp + strlen("+CMGR: \"REC READ\",\""), MAX_NUM_LEN-1 );
/* Search closing double quote and cut the string where it's found */
for( pTmp=number; *pTmp != '\0'; pTmp++ )
{
if(*pTmp == '"')
{
*pTmp = '\0';
break;
}
}
}
/* Get SMS Text. Starting after first \r\n */
pTmp = strstr( exampleCGMRResponse, "\r\n" );
if( pTmp )
{
strncpy( text, pTmp + strlen("\r\n"), MAX_SMS_LEN-1 );
/* Search closing string "\r\n\r\nOK" */
pTmp = strstr( text, "\r\n\r\nOK" );
if( pTmp )
{
*pTmp = '\0';
}
}
printf( "Message sender number is \"%s\"\n", number );
printf( "Message text is \"%s\"\n", text );
return 0;
}
As you might notice:
Data section of the command is searched after first \r\n pattern. In fact, by AT commands standard, the main response cannot contain that pattern
AT commands responses data sections are always closed by \r\n\r\nOK pattern.
Improvements left to the implementations of the asker (in order to simplify my answer):
\r\n\r\nOK pattern should be searched starting from the end of the response
A preliminary filter can be done on status parameter of +CGMR response, in this example hardcoded to "\"REC READ\"". It can have also values "\"REC UNREAD\"", "\"STO SENT\"" and "\"STO UNSENT\"".
The output of this implementation is
Message sender number is "+18142150657"
Message text is "Message 1"
Parse line by line using sscanf
Something like this for the number:
#include <stdio.h>
// ...
// Do this for each line
size_t num;
sscanf( line_string, "+CMGR: \"REC READ\",\"+%lu\",", &num);
Some general considerations
You can store the phone number in a char array. This can have a size of 20 to store all possible cases.
The message is stored in another char array. The max size needs to be determined from the manual of your GMS module. I have assumed this as 200.
The input data is read into a char array. The conversion function below is called after checking for Error
The conversion function can return a boolean to inform whether the conversion is successful.
Assumption that the input string will always have same format (i.e. number of commas) spaces etc.
You can then do tokenization using strtok based on the , and then strip the starting and ending "
To get the message tokenize upto the last ,. Then read the length of the message and also the entire message
Here below is a general idea of how to carry this out for the phone number
typedef struct
{
char number[20];
char message[200];
// Add here for other fields.
} MessageType;
bool ParseMessage (char * input, MessageType *pOutput)
{
char *tok;
int parametercount = 0,len;
tok = strtok(input, ",");
while (tok != NULL)
{
switch (parametercount)
{
case 2: //number
len = strlen(tok);
if (len >= 9) // can check for minimum number size
{
strcpy(pOutput->number,tok+1);
// delete the last `"`
if (pOutput->number[len-1] == '"')
{
pOutput->number[len-1] = '\0';
}
else
{
return false;
}
}
break;
default:
break;
}
parametercount++;
tok = strtok(NULL, ",");
}
if (paremetercount == 10)
return true;
else
return false;
}
I am trying to read a textfile like this
1234567890 1234
9876543210 22
into a List struct in my program. I read in the files via fgets() and then use strtok to seperate the numbers, put them into variables and then finally into the List. However, I find that in doing this and printing the resulting strings, strtok always takes the final string in the final line to be NULL, thus resulting in a segmentation fault.
fgets(fileOutput,400,filePointer); //Read in a line from the file
inputPlate = strtok(fileOutput," "); // Take the first token, store into inputPlate
while(fileOutput != NULL)
{
string = strtok(NULL," ");
mileage = atoi(string); //Convert from string to integer and store into mileage
car = initializeCar(mileage,dateNULL,inputPlate);
avail->next = addList(avail->next,car,0);
fgets(fileOutput,400,filePointer);
inputPlate = strtok(fileOutput," ");
}
How do I resolve this?
Reading a text file line by line with fgets() is good.
Not checking the return value of fgets() is weak. This caused OP's code to process beyond the last line.
// Weak code
// fgets(fileOutput,400,filePointer); //Read in a line from the file
// ...
// while(fileOutput != NULL)
// {
Better to check the result of fgets() to determine when input is complete:
#define LINE_SIZE 400
...
while (fgets(fileOutput, LINE_SIZE, filePointer) != NULL)
{
Then process the string. A simple way to assess parsing success to is to append " %n" to a sscanf() format to record the offset of the scan.
char inputPlate[LINE_SIZE];
int mileage;
int n = -1;
sscanf(fileOutput, "%s%d %n", inputPlate, &mileage, &n);
// Was `n` not changed? Did scanning stop before the string end?
if (n < 0 || fileOutput[n] != '\0') {
Handle_Bad_input();
break;
} else {
car = initializeCar(mileage, dateNULL, inputPlate);
avail->next = addList(avail->next,car,0);
}
}
You could write a simpler parser with fscanf():
FILE *filePointer;
... // code not shown for opening the file, initalizing the list...
char inputPlate[32];
int mileage;
while (fscanf(filePointer, "%31s%d", inputPlate, &mileage) == 2) {
car = initializeCar(mileage, dateNULL, inputPlate);
avail->next = addList(avail->next, car, 0);
}
I am new to Unicode/UTF8 representations of strings. I am trying to read a UTF8 encoded file, separate it with spaces and then print every character/code-point in every word (separated by spaces).
I was able to use wchar_t (I know it uses utf16 or utf32(?) internally) for reading text from the file, printing it and writing it to another file. However, I was unable to use the wchar_t to get either a substring or traverse it element by element.
To solve for this, I used the ICU library from IBM. Code:
while (fgetws(readString, 1000, wifile) != NULL) {
wprintf(L"String: %s\n", readString);
//split string on the base of spaces.
wchar_t *nextToken = NULL;
wchar_t *token = wcstok_s(readString, L" ", &nextToken);
UChar *utf8Token = (UChar *)token;
u_printf("Token in UChar: %S\n", utf8Token);
while (token != NULL) {
printf("Hello.\n");
fwprintf(wofileString, L"%ls and length: %d\n", token, wcslen(token));
fwprintf(wofileString, L"UTF8 rep:%s and length: %d\n", utf8Token, u_strlen(utf8Token));
int32_t counter = 0;
for (counter = 0; counter < u_strlen(utf8Token);) {
UChar32 ch;
U8_NEXT(utf8Token, counter, u_strlen(utf8Token), ch);
fwprintf(wofileString, L"Token[%d] = ", counter);
if (ch < 127) {
printf("Less than 127.\n");
if (ch > 1) {
printf("Printing%d.\n", ch);
u_fprintf((UFILE *)wofileString, "%c\n", (UChar)ch);
}
} else if (ch == CharacterIterator::DONE) {
printf("Done.\n");
u_fprintf((UFILE *)wofileString, "[CharacterIterator::DONE]\n");
} else {
printf("More than 127.\n");
u_fprintf((UFILE *)wofileString, "[%X]\n", ch);
}
}
token = wcstok_s(NULL, L" ", &nextToken);
utf8Token = (UChar *)token;
counter = 0;
}
fputws(L"Complete String: ", wofileString);
fputws(readString, wofileString);
fputws(L"\n", wofileString);
}
This program always stops working when it gets to the part where the characters are printed.
My questions:
1. How can I print all the 'characters' in the input UTF8 string?
2. Is the conversion: UChar *utf8Token = (UChar *) token; even correct? Given that the internal representation for token is UTF16 or UTF32?
3. Where am I going wrong?
4. How do I get a substring of the string?
fwprintf(wofileString,…
u_fprintf((UFILE *)wofileString,…
One of these two lines is wrong, depending on what wofileString actually is.
I'd recommend just using the u_… functions.
In fact, I'd just use u_printf("string", …) or u_printf_u(L"String", …) instead of fwprintf or fputws.
I'm trying to take some input from a text file, put it into a structure and print it out. The sample text file looks like this:
2
Curtis
660-------
Obama
2024561111
(Digits on the first number dashed out (for privacy), second is the Whitehouse.gov one, I called, they can't help me.)
Sample output:
204-456-1111 Obama
660--------- Curtis
(Formatting and sorting shouldn't be a problem when I figure out the rest.)
My question is labeled by the question marks below (in the first FOR loop, how do I get specific lines out of the text file to create the structures?
#include <stdio.h>
#include <string.h>
struct telephone {
char name[80];
long long int number;
}
main() {
struct telephone a, b;
char text[80];
int amount, i;
FILE *fp;
fp = fopen("phone.txt", "r");
fscanf(fp, "%d", amount);
struct telephone list[amount];
for(i = 0; i < amount; i++) {
strcpy(list[i].name, ???);
list[i].number, ???);
}
fclose(fp);
for(i = 0; i < amount; i++) {
DisplayStruct(list[i]);
}
}
DisplayStruct(struct telephone input) {
printf("%lld %s\n", input.number, input.name);
}
Use fgets to read one line at a time.
int lnum = 0;
char line[100];
while( fgets(line, sizeof(line), fp) ) {
lnum++;
printf( "Line %d : %s\n", lnum, line );
}
You can then use sscanf or strtok or numerous other approaches to pull data out of the string you just read.
I advise against storing your phone number as an integer. Phone numbers are better represented as strings.
If you can guarantee that neither names nor phone numbers have blanks in them, you can utilize fscanf() to read this data:
for(i = 0; i < amount; i++) {
fscanf("%s %lld", list[i].name, &list[i].phone);
}
Things to keep in mind:
You must check for conversion errors
This approach is less tolerant to input errors (in case of using fgets() it might be easier to recover and drop the malformed entry - unless the record has wrong number of fields).
Agree with #paddy, use a string to store phone numbers. (Cope with leading 0s, variant length, #, *, pause, etc.). Might as well also make sure it is big enough for a int64_t.
Note: The web has examples of 22 digits.
struct telephone {
char name[80];
char number[21];
}
To read in the data ...
for (i = 0; i < amount; i++) {
// +1 for buffer size as string read has a \n which is not stored.
char na[sizeof list[0].name + 1];
char nu[sizeof list[0].number + 1];
if ((fgets(na, sizeof na, fp) == NULL) || (fgets(nu, sizeof nu, fp) == NULL)) {
break; // TBD, Handle unexpected missing data
}
// The leading space in the format will skip leading white-spaces.
if (1 != sscanf(na, " %79[^\n]", list[i].name)) {
break; // TBD, Handle empty string
}
if (1 != sscanf(na, " %20[^\n]", list[i].number)) {
break; // TBD, Handle empty string
}
}
if (fgetc(fp) != EOF) {
; // Handle unexpected extra data
}
amount = i;
To write
// Pass address of structure
for(i = 0; i < amount; i++) {
DisplayStruct(&list[i]);
}
void DisplayStruct(const struct telephone *input) {
if (strlen(input->number) == 10) {
printf("%.3s-%.3s-%4s", input->number, &input->number[3], &input->number[6]);
}
else { // tbd format for unexpected telephone number length
printf("%13s", input->number);
}
// Suggest something around %s like \"%s\" to encapsulate names with spaces
printf(" %s\n", input->name);
}