Sorting strings with qsort does not work - c++

I have a program, that asks several strings and should sort them.
My code is:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX_STR_LEN 256
int myStrCmp (const void * a, const void * b)
{
return strcmp((const char *)a, (const char *)b);
}
int main(void)
{
int strNum; // expected number of input strings
int strCnt; // counter of strings
char ** storage; // pointr to the memory when strings are stored
char strBuf[ MAX_STR_LEN]; // buffer for strings
char * strPtr;
// input of strings number
do{
printf("How many strings will be entered: ");
while( scanf("%d", &strNum) != 1)
{
printf("ERROR: Not number was entered!\n");
while( getchar() != '\n' );
printf("Please enter a number: ");
}
if( strNum < 2 )
{
printf("ERROR: Number less than 2 was entered!\n");
}
while( getchar() != '\n' );
}
while(strNum < 2);
// allocation of memory for pointers
storage = (char **) calloc(strNum, sizeof(char*) );
if( storage == NULL )
{
printf("ERROR: Unexpected problems with memory allocation!\n");
return 1;
}
// input of strings
for( strCnt = 0; strCnt < strNum; strCnt++)
{
printf("Enter string #%d:\n", strCnt + 1);
fgets(strBuf, MAX_STR_LEN, stdin);
strPtr = strchr(strBuf, '\n');
if( strPtr )
{
*strPtr = '\0';
}
else
{
strBuf[ MAX_STR_LEN - 1] = '\0';
}
// allocation memory for particular string
storage[strCnt] = (char *) malloc(strlen(strBuf) + 1);
if(storage[strCnt] == NULL)
{
printf("ERROR: Unexpected problems with memory allocation!\n");
return 2;
}
// move string to dynamic memory
strcpy(storage[strCnt], strBuf);
}
// sort the strings
qsort(storage, strNum, sizeof(char**), myStrCmp);
// output the result
printf("\nSorted strings:\n");
for( strCnt = 0; strCnt < strNum; strCnt++)
{
printf("%s\n", storage[strCnt]);
}
return 0;
}
The simplest test shows the trouble:
How many strings will be entered: 3
Enter string #1:
ddd
Enter string #2:
aaa
Enter string #3:
ccc
Sorted strings:
ddd
aaa
ccc
I have tryed Visual C++ and gcc, but the result is the same. Please, say me what is wrong in the code?

The problem is in myStrCmp function.
Because a and b are elements in not a simple array, but in array of pointers, their type must be char ** and function that compares two elements have to be as follows:
int myStrCmp (const void * a, const void * b)
{
return strcmp(*(const char **)a, *(const char **)b);
}

Related

How to split a string by another string in Arduino?

I have a character array like below:
char array[] = "AAAA... A1... 3. B1.";
How can I split this array by the string "..." in Arduino? I have tried:
ptr = strtok(array, "...");
and the output is the following:
AAAA,
A1,
3,
B1
But I actually want output to be
AAAA,
A1,
3.B1.
How to get this output?
edit:
My full code is this:
char array[] = "AAAA... A1... 3. B1.";
char *strings[10];
char *ptr = NULL;`enter code here`
void setup()
{
Serial.begin(9600);
byte index = 0;
ptr = strtok(array, "..."); // takes a list of delimiters
while(ptr != NULL)
{
strings[index] = ptr;
index++;
ptr = strtok(NULL, "..."); // takes a list of delimiters
}
for(int n = 0; n < index; n++)
{
Serial.println(strings[n]);
}
}
The main problem is that strtok does not find a string inside another string. strtok looks for a character in a string. When you give multiple characters to strtok it looks for any of these. Consequently, writing strtok(array, "..."); is exactly the same as writing strtok(array, ".");. That is why you get a split after "3."
There are multiple ways of doing what you want. Below I'll show you an example using strstr. Unlike strtokthe strstr function do find a substring inside a string - just what you are looking for. But.. strstr is not a tokenizer so some extra code is required to print the substrings.
Something like this should do:
int main()
{
char array[] = "AAAA... A1... 3. B1...";
char* ps = array;
char* pf = strstr(ps, "..."); // Find first substring
while(pf)
{
int len = pf - ps; // Number of chars to print
printf("%.*s\n", len, ps);
ps = pf + 3;
pf = strstr(ps, "..."); // Find next substring
}
return 0;
}
You can implement your own split as strtok except the role of the second argument :
#include <stdio.h>
#include <string.h>
char * split(char *str, const char * delim)
{
static char * s;
char * p, * r;
if (str != NULL)
s = str;
p = strstr(s, delim);
if (p == NULL) {
if (*s == 0)
return NULL;
r = s;
s += strlen(s);
return r;
}
r = s;
*p = 0;
s = p + strlen(delim);
return r;
}
int main()
{
char s[] = "AAAA... A1... 3. B1.";
char * p = s;
char * t;
while ((t = split(p, "...")) != NULL) {
printf("'%s'\n", t);
p = NULL;
}
return 0;
}
Compilation and execution:
/tmp % gcc -g -pedantic -Wextra s.c
/tmp % ./a.out
'AAAA'
' A1'
' 3. B1.'
/tmp %
I print between '' to show the return spaces, because I am not sure you want them, so delim is not only ... in that case
Because you tagged this as c++, here is a c++ 'version' of your code:
#include <iostream>
using std::cout;
using std::endl;
#include <vector>
using std::vector;
#include <string>
using std::string;
class T965_t
{
string array;
vector<string> strings;
public:
T965_t() : array("AAAA... A1... 3. B1.")
{
strings.reserve(10);
}
~T965_t() = default;
int operator()() { return setup(); } // functor entry
private: // methods
int setup()
{
cout << endl;
const string pat1 ("... ");
string s1 = array; // working copy
size_t indx = s1.find(pat1, 0); // find first ... pattern
// start search at ---------^
do
{
if (string::npos == indx) // pattern not found
{
strings.push_back (s1); // capture 'remainder' of s1
break; // not found, kick out
}
// else
// extract --------vvvvvvvvvvvvvvvvv
strings.push_back (s1.substr(0, indx)); // capture
// capture to vector
indx += pat1.size(); // i.e. 4
s1.erase(0, indx); // erase previous capture
indx = s1.find(pat1, 0); // find next
} while(true);
for(uint n = 0; n < strings.size(); n++)
cout << strings[n] << "\n";
cout << endl;
return 0;
}
}; // class T965_t
int main(int , char**) { return T965_t()(); } // call functor
With output:
AAAA
A1
3. B1.
Note: I leave changing "3. B1." to "3.B1.", and adding commas at end of each line (except the last) as an exercise for the OP if required.
I looked for a split function and I didn't find one that meets my requirement, so I made one and it works for me so far, of course in the future I will make some improvements, but it got me out of trouble.
But there is also the strtok function and better use that.
https://www.delftstack.com/es/howto/arduino/arduino-strtok/
I have the split function
Arduino code:
void split(String * vecSplit, int dimArray,String content,char separator){
if(content.length()==0)
return;
content = content + separator;
int countVec = 0;
int posSep = 0;
int posInit = 0;
while(countVec<dimArray){
posSep = content.indexOf(separator,posSep);
if(posSep<0){
return;
}
countVec++;
String splitStr = content.substring(posInit,posSep);
posSep = posSep+1;
posInit = posSep;
vecSplit[countVec] = splitStr;
countVec++;
}
}
Llamada a funcion:
smsContent = "APN:4g.entel;DOMAIN:domolin.com;DELAY_GPS:60";
String vecSplit[10];
split(vecSplit,10,smsContent,';');
for(int i = 0;i<10;i++){
Serial.println(vecSplit[i]);
}
String input:
APN:4gentel;DOMAIN:domolin.com;DELAY_GPS:60
Output:
APN:4g.entel
DOMAIN:domolin.com
DELAY_GPS:60
RESET:true
enter image description here

Extract fields and values from text line using C++

I am reading in a text file with lines of the format:
date =20170422,line =10,index =3,field =partType,lock =productCode1,bookmark=2/19/56,
I need to extract the name of the field (date, line, index, etc.) and its corresponding value into char field[] and char value[] variables. If necessary, I am allowed to modify the format of the lines.
My initial thinking was to use while loops and check for = and , characters but it was getting messy and it seems like there may be a cleaner way.
You could do something like the below example. Split the string by commas using getline from your file, then split use an istringstream to and getline to split it again by an equals sign.
#include<iostream>
#include<fstream>
#include<string>
#include<sstream>
int main()
{
std::ifstream file("test.txt");
std::string wholeLine, partOfLine;
while(std::getline(file, wholeLine, ',')) {
std::istringstream wholeLineSS(wholeLine);
while(std::getline(wholeLineSS, partOfLine, '=')) {
std::cout<<partOfLine<<std::endl;
}
}
return 0;
}
The program I post here extracts the parameters from one or more strings which are formatted as you require. The function extract extracts all the parameters contained in a string (of the format you specified) and insert their names and values in a structure (struct sParms) array.
You may compile the program as extract and execute it at the system prompt as:
username: ./extract "date =20170422,line =10,index =3,field
=partType,lock =productCode1,bookmark=2/19/56,"
The output will be the following:
[date]=[20170422]
[line]=[10]
[index]=[3]
[field]=[partType]
[lock]=[productCode1]
[bookmark]=[2/19/56]
You may execute the program with more than one string:
username: ./extract "date =20170422,line =10,index =3,field
=partType,lock =productCode1,bookmark=2/19/56," "yes=1, no=0"
The output will be the following:
[date]=[20170422]
[line]=[10]
[index]=[3]
[field]=[partType]
[lock]=[productCode1]
[bookmark]=[2/19/56]
[yes]=[1]
[no]=[0]
In the following line there's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
//Parameters: date =20170422,line =10,index =3,field =partType,lock =productCode1,bookmark=2/19/56,
#define SEPARATOR ','
#define ASSIGNEMENT '='
typedef struct sParms {
char * fieldName;
char * fieldValue;
} tsParms;
int loadString(char *to, const char *from);
int extract(tsParms **oparms, const char *inb);
// Retrieve buffer length
int loadString(char *to, const char *from)
{
int len=0;
while(*from<=32 && *from!=SEPARATOR && *from!=ASSIGNEMENT)
from++;
// Get the string value
while(*from>32 && *from!=SEPARATOR && *from!=ASSIGNEMENT) {
*(to+len++)=*from;
++from;
}
*(to+len++)=0;
return len;
}
int extract(tsParms ** oparms, const char *inb)
{
int cnt=0,j;
const char * end, *equ, *start;
char * buff;
tsParms * parms;
if (inb == NULL || strlen(inb) == 0 || oparms == NULL)
return 0;
// It counts the number of parms
end=strchr(inb,ASSIGNEMENT);
while(end) {
cnt++;
end=strchr(end+1,ASSIGNEMENT);
}
if (!cnt)
return 0;
/* Doing some considerations we may assume that the memory to use to store
* fields name and values is the same of the input string (inb)
*
* The space to store the pointers is cnt * sizeof(tsParms *).
*/
j=cnt * sizeof(tsParms) + strlen(inb);
parms = malloc(j+1);
memset(parms,0,j+1);
buff = (char *)(parms+cnt); // The memory area where we can save data!
start=inb;end=start;cnt=0;
do {
end=strchr(start,SEPARATOR);
equ=strchr(start,ASSIGNEMENT);
if (equ) {
//Get the field name
parms[cnt].fieldName=buff;
buff+=loadString(buff,start);
//Get the field value
start=equ+1;
parms[cnt].fieldValue=buff;
buff+=loadString(buff,start);
cnt++;
}
if (end)
start=end+1;
} while(end);
*oparms = parms;
return cnt;
}
int main(int argc, char *argv[])
{
int i,j,cnt=0,retval=0;
tsParms * parms=NULL;
if (argc<2) {
printf("Usage: %s \"string-1\" [\"string-2\" ...\"string-n\"]\n",basename(argv[0]));
return 1;
}
for(i=1; i<argc; i++) {
cnt=extract(&parms, argv[i]);
if (cnt!=0 && parms!=NULL) {
for(j=0;j<cnt;j++) {
printf("[%s]=[%s]\n",parms[j].fieldName,parms[j].fieldValue);
}
puts("");
free((void *)parms);
} else {
retval=1;
break;
}
}
return retval;
}

How to edit an element in a streaming char array

I have records coming in from fgets (web data using popen(), not a file) that are in const char * array format.
const char * cstr = buff;
The 3rd item is a string and needs to either be removed or changed to a zero.
How do I access the 3rd element in a const char * array stream in increments of 5?
1
2
string
4
5
1
2
string
4
5
code:
while(fgets(buff, sizeof buff, fp) != NULL)
{
const char * cstr2 = buff;
for(int i = 0; i < 5; ++i){
if(i == 3){
if (!strcmp(cstr2, "Buy\n")) {
printf("Found One!\n");
cstr2[2] = 0;
}
if (!strcmp(cstr2, "Sell\n")) {
printf("Found Two!\n");
cstr2[2] = 0;
}
}
}
}
expected output:
1
2
0
4
5
1
2
0
4
5
error:
no match and :
error: assignment of read-only location '*(cstr2 + 2u)'
How do you correctly access the 3rd element in a char streaming char array?
This solution was previously posted by anonymous:
char* getmyData()
{
char buff[BUFSIZ];
FILE *fp = popen("php getMyorders.php 155", "r");
if (fp == NULL) perror ("Error opening file");
size_t size = 1000;
char* data = (char*)malloc(size);
char* ptr = data;
std::string::size_type sz;
int i=0;
while(fgets(buff, sizeof(buff), fp) != NULL)
{
const char * cstr2 = buff;
const char* test = ptr;
//for(int i = 0; i < 5; ++i)
{
if(i == 2){
if (!strcmp(cstr2, "Buy\n")) {
printf("Found One!\n");
strcpy(ptr,"0\n");
//ptr+=2;
}
else
if (!strcmp(cstr2, "Sell\n")) {
printf("Found Two!\n");
strcpy(ptr,"0\n");
//ptr+=2;
}
else
{
strcpy(ptr,cstr2);
ptr+=strlen(cstr2);
}
}
else
{
strcpy(ptr,cstr2);
ptr+=strlen(cstr2);
}
try
{
int nc = std::stod (test,&sz);
std::cout << "Test: " << 1+nc << "\n";
}
catch(...)
{
}
i++;
if (i==5)
i=0;
}
if (ptr-data+100>=size)
{
int ofs = ptr-data;
size*=2;
data = (char*)realloc(data,size);
ptr = data+ofs;
}
}
return data; // DONT FORGET TO call free() on it
}
From your sample code it is not clear what is the expected output. Is it an array of integer ? a formatted text string ? a byte array ? we can't know.
Assuming you have a text formatted input and want a text formatted output, a simple solution is to write a new string with the correct values and not try to modify your input buffer.
If you know the exact format of your input records you could use fscanf to do the parsing instead of doing it by hand. And you could use ssprintf to do the formatting of the output string.
As others pointed out, if you can use C++, you'd have safer/easier options. Please comment about your willingness to use C++.

Value type const char cannot be used to initialize an entity of type char*

I am having the following problem with my code, though it compiles correctly:
value type const char cannot be used to initialize an entity of type char*
Can someone help me? I can run the code which is weird but I can't create a makefile using this. It's very weird to me.
int SpliString(struct dict_word *entry, const char *str)
{
long word_length,j,k;
int yearIndex;
char *buffer;
char *endOfYears;
char *endOfYear;
char *endOfDefinition;
char *endOfWord = strstr(str, "_#_");
//Sets the first num bytes of the block of memory pointed by ptr
//to the specified value (related as an unsigned char)
memset(entry, 0, sizeof(struct dict_word));
// If '_#_' is not found, it's NULL
if (endOfWord)
{
// Calculating word legth; 'str' points to start of word, 'endofWord' points to '_#_' that is just after word
word_length = endOfWord - str;
// Copying data into the word
strncpy(entry->words, str, word_length);
// 'endOfYears' points to '_#_,' but wee need to find follow '_#_'
// therefore there is added 3 in order to skip curremnt '_#_
endOfYears = strstr(endOfWord+3, "_#_");
if (endOfYears)
{
word_length = endOfYears - (endOfWord+3);
// Skips _#_
buffer = endOfWord+3;
yearIndex = 0;
j = 0;
// Finds next year in the line, it stops if all 10 years is filled
// or end of years string is reached
while(yearIndex<10 && buffer+j<endOfYears)
{
// Stores year in the buffer, with converting 'stirng' to 'int'
entry->year[yearIndex] = atoi(buffer+j);
// check year for negative...
if (entry->year[yearIndex]<=0)
return 0;
// Locating substring; 'j' is current offset from beginning of buffer
endOfYear = strchr(buffer+j, '_');
if (endOfYear)
{
j = endOfYear - buffer;
j++;
yearIndex++;
}
else
{
break;
}
}
//endOfYears points to '_#_' that separatates 'years' and 'definition'
//and there is needed to find '_#_' between 'definition' and 'synonyms'
//therefore it skips '_#_' that separatates 'years' and 'definition',
//+3, because '_#_' has length = 3
endOfDefinition = strstr(endOfYears+3, "_#_");
if (endOfDefinition)
{
word_length = endOfDefinition - (endOfYears+3);
k = 0;
for(j=0; j<word_length; j++)
{
// Skips '_#_'
if (endOfYears[j+3]==',')
{
entry->eng_synonyms[k] = ' ';
k++;
}
else if (endOfYears[j+3]>='a' && endOfYears[j+3]<='z')
{
entry->eng_synonyms[k] = endOfYears[j+3];
k++;
}
else if (endOfYears[j+3]!='_')
{
return 0;
}
}
k = 0;
word_length = (str+strlen(str)) - (endOfDefinition+3);
for(j=0; j<word_length; j++)
{
if (endOfDefinition[j+3]==',')
{
entry->heb_synonyms[k] = ' ';
k++;
}
else if (endOfDefinition[j+3]>='A' && endOfDefinition[j+3]<='Z')
{
entry->heb_synonyms[k] = endOfDefinition[j+3];
k++;
}
else if (endOfDefinition[j+3]!='_')
{
return 0;
}
}
}
// Check for legality
// Check all symbols of 'entry->words'
// calculate length and supress warning
for(j=0;j<(int)strlen(entry->words);j++)
{
if (entry->words[j]<'a' || entry->words[j]>'z')
return 0;
}
return 1;
}
}
return 0;
}
Use
const char *buffer;
const char *endOfWord = strstr(str, "_#_");
Confident OP is compiling in C++.
// C
char *strstr(const char *s1, const char *s2);
// C++
const char* strstr(const char* s1, const char* s2);
char* strstr( char* s1, const char* s2);
See
Compile C app with Visual Studio 2012
How to compile and execute C program on Visual Studio 2012 for Windows 8?

I get a number 2 when I reverse my string

I wrote this code to reverse strings. It works well, but when I enter short strings like "american beauty," it actually prints "ytuaeb nacirema2." This is my code. I would like to know what is wrong with my code that prints a random 2 at the end of the string. Thanks
// This program prompts the user to enter a string and displays it backwards.
#include <iostream>
#include <cstdlib>
using namespace std;
void printBackwards(char *strPtr); // Function prototype
int main() {
const int SIZE = 50;
char userString[SIZE];
char *strPtr;
cout << "Please enter a string (up to 49 characters)";
cin.getline(userString, SIZE);
printBackwards(userString);
}
//**************************************************************
// Definition of printBackwards. This function receives a *
// pointer to character and inverts the order of the characters*
// within it. *
//**************************************************************
void printBackwards(char *strPtr) {
const int SIZE = 50;
int length = 0;
char stringInverted[SIZE];
int count = 0;
char *strPtr1 = 0;
int stringSize;
int i = 0;
int sum = 0;
while (*strPtr != '\0') {
strPtr++; // Set the pointer at the end of the string.
sum++; // Add to sum.
}
strPtr--;
// Save the contents of strPtr on stringInverted on inverted order
while (count < sum) {
stringInverted[count] = *strPtr;
strPtr--;
count++;
}
// Add '\0' at the end of stringSize
stringInverted[count] == '\0';
cout << stringInverted << endl;
}
Thanks.
Your null termination is wrong. You're using == instead of =. You need to change:
stringInverted[count] == '\0';
into
stringInverted[count] = '\0';
// Add '\0' at the end of stringSize
stringInverted[count] == '\0';
Should use = here.
What is wrong with your code is that you do not even use strlen for counting the length of the string and you use fixed size strings (no malloc, or, gasp new[]), or the std::string (this is C++)! Even in plain C, not using strlen is always wrong because it is hand-optimized for the processor. What is worst, you have allocated the string to be returned (stringInverted) from the stack frame, which means when the function exits, the pointer is invalid and any time the code "works" is purely accidental.
To reverse a string on c++ you do this:
#include <iostream>
#include <string>
int main() {
std::string s = "asdfasdf";
std::string reversed (s.rbegin(), s.rend());
std::cout << reversed << std::endl;
}
To reverse a string in C99 you do this:
char *reverse(const char *string) {
int length = strlen(string);
char *rv = (char*)malloc(length + 1);
char *end = rv + length;
*end-- = 0;
for ( ; end >= rv; end --, string ++) {
*end = *string;
}
return rv;
}
and remember to free the returned pointer after use. All other answers so far are blatantly wrong :)