c strncasecmp that can handle NULL (character 0) - c

I have strings that may contain character 0. They are stored in a structure like this:
typedef struct somestruct_s {
const unsigned char *string;
size_t length;
};
If I wish to compare 2 of these together I can use memcmp as such:
int match = (a->length == b->length) ? !memcmp (a->string, b->string, a->length) : 0;
But if I wish to compare 2 of these together without regard to case, my first instinct is to use strncasecmp/_strnicmp -- however, that function stops on null characters.
Is there a common C function already around that can do this. I don't mind writing my own, but before I do I want to make sure there isn't a standard function that I am unaware of.

Related

Trying to use Struct to make a String array and comparing it to a String

#include <stdio.h>
typedef struct Name {
char Name[10];
}Name;
A(Name a) {
Name NameList[16] = {"", "Holly", "Zia", "Brandon","Jerry","Tom","Katie","Klein","Sophie","Brian","Mary","Ben","Annie","Percy","Zoe","Zack"};
for (int i = 0; i <= 15; i++) {
if (a = NameList[i]) {
for (int k = i; k <= 15; k++) {
printf(NameList + k);
}
break;
}
else {
continue;
}
}
}
I'm getting problems with if (a = NameList[i]) this part.. compiler tells me that I need pointers, but I'm not sure where I have to put them.. help?
a = NameList[i] is an assignment, not comparison. In general you need to use == for comparison.
In C a string is an array of chars, zero terminated. In order to compare strings in C you need to use strcmp. Note that it returns 0 if the strings are equal.
a is not actually a string, but contains one. a.Name is a string. Same applies to NameList[i].
A struct should not contain a member with the same name as the struct itself (even if your compiler accepts it, it is error-prone). You should rename the member Name (e.g. to data).
Therefore after modifying the struct definition, you should change your if to:
if (strcmp(a.data, NameList[i].data) == 0)
First of all the statement if (a = NameList[i]) {bla..} does not do what you think it does. It first executes the command a = NameList[i] and then evaluates if (a != 0) in which case the commands inside the if statement will be executed.
That's a mistake everyone has made at some point ,even more experienced programmers might make this mistake from time to time.
Now I see you are trying to compare two strings ,in C we do this using the function strcmp(a ,b) .If strcmp returns 0 ,the two strings you gave it are equal.
Another mistake you made ,is that you are comparing two structs instead of their contents. You have to use if(!strcmp(a.Name ,NameList[i].Name)) instead.
So your main issue is that you are comparing structs not their contents.

Turning array in main into a global, to later be altered by main?

I have two arrays of the alphabet in the following format:
const char plain[26] = {'a','b',....'y','z'} // this is the the full alphabet
const char crypt[26] = {'i','d',....'m','x'}; // this is the the alphabet scrambled
The order of the alphabet in both arrays can change depending on input. This change happens in the main function.
The purpose of this is to match the letters of the string to the second, like an encryption. I compare characters to array values. So it would kind of look like this (simplified)
text[3] = 'yes';
changed[3];
if(text[0] == plain[25]){ //would be done under a for loop so 25 would be a changing integer value
changed[0] = [crypt[25];
}
My code works under perfectly under the main function. I wanted to mention my purpose like this because I was having previous problems simply due to the type of array and formatting. And since the array is moved outside, I will probably/am running into those problems again.
Now I want to make the arrays global. The actual encryption happens in a function that does not take the arrays as a variable. But I want the function to have access to them.
Here's what it looks likes right now
const char plain[26];
const char crypt[26];
int maint(void){
const char plain[26] = {'a','b',....'y','z'} \\values get changed here
const char crypt[26] = {'i','d',....'m','x'} \\and here
While this provides no errors, I dont get an output, I believe the other functions are using a blank array instead of the changed one (if the change even worked).
I've tried different array types, I believe the issue is in initialization or giving the array values.
Edit: To clarify, the two arrays can be in any order. A text file will randomize the order can give it to me in the format:
b,r
m,o
l,s
...
...
...
Both cases the alphabet is randomized. Where the first column would correspond to the first array (plain), second would be to second array (crypt).
If there's a way to read by columns and store in the format
plain = 'bml...'; \\whole alphabet randomized
crypt = 'ros...'; \\whole alphabet randomized
That would also work.
The plain and crypt you have in main aren't the same as the global ones. Since you declare them again, they're new ones that are only visible in main. Thus you're not changing the global ones.
Instead, only declare them once globally and do assignment in the main function:
char plain[26];
int main(void) {
memcpy(plain, "abcdefghijklmnopqrstuvwxyz", 26); //values get changed here
return 0; // indicate successful program execution
}
Also note that there are some syntax errors in
const char plain[26] = {'a','b',....'y','z'} \\values get changed here
Comments start with //, not \\, and you need a ; after a statement. Also, the int main should return an int in C.
Of course, if you don't need to actually change the memory and only assign it to predefined sets of characters, you can do it like this:
const char *plain;
int main(void) {
plain = "abcdefghijklmnopqrstuvwxyz";
return 0;
}
This way you can still read from it with syntax like plain[5], but you can't assign to it with, say plain[5] = 'a';.
Remove "const char" before plain and crypt arrays in main function to see the actual issue.
“Const Char” before plan and crypt arrays actually declare two new local char array constants with same name. As “const” char array can only be initialized at the declaration time therefore initialization in main does not throw error because they are not same global conts arrays.
Instead of using const use Const pointer as suggested in below answer
Another way to look at it is that plain and crypt will both decay to pointers on access (subject to the exceptions in) C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3). So why not just use global pointers to begin with that can then be reassigned as needed throughout your code?
This provides the flexibility of assignment from (1) string literals; (2) constant arrays; or (3) from compound literal initializers. You can #define a constant for the size (number of characters) plain and crypt point to.
For example:
#include <stdio.h>
#define NC 3 /* number of chars (26 for you) */
const char *plain, *crypt; /* global pointers */
void prnchars (const char *arr)
{
for (int i = 0; i < NC; i++)
putchar (arr[i]);
putchar ('\n');
}
int main (void) {
plain = "abc"; /* assigning literals */
crypt = "def";
prnchars (plain);
prnchars (crypt);
crypt = "ghi"; /* reassign at will */
prnchars (crypt);
const char foo[] = "jkl"; /* constant arrays */
crypt = foo;
prnchars (crypt);
crypt = (const char[]){'m', 'n', 'o'}; /* compound literals */
prnchars (crypt);
return 0;
}
Example Use/Output
$ ./bin/global_ptr
abc
def
ghi
jkl
mno
It's just another way of looking at the problem.

How to use array of structures in while loop

I have created a typedef structure as shown below which consists of 4 fields
and i have inserted as array of structures which can be seen below
typedef struct Signal {
long vr;
char name[20];
char Type[20];
char casuality[20];
} Signal;
I have used array of structures as shown below
void xmlRead()
{
int i;
Signal var[4];
var[0].vr=1073741824;
strcpy(var[0].name,"Ball1_pos");
strcpy(var[0].Type,"Real");
strcpy(var[0].casuality,"output");
var[1].vr=1073741825;
strcpy(var[1].name,"Ball2_pos");
strcpy(var[1].Type,"Real");
strcpy(var[1].casuality,"output");
var[2].vr=1073741826;
strcpy(var[2].name,"Ball1_vel");
strcpy(var[2].Type,"Real");
strcpy(var[2].casuality,"output");
var[3].vr=1073741827;
strcpy(var[3].name,"Ball2_vel");
strcpy(var[3].Type,"Real");
strcpy(var[3].casuality,"output");
for(i=0; i<=3; i++)
{
while(var[i].casuality="output") **//Here it is showing error as expression must have modifiable lvalue//**
{
//Some statements
}
}
}
Edited
Its showing error because you are assigning(i assume u meant to compare here) the base address of the array casuality to the string "output"
Another thing is you cannot use == to compare strings because it will always compare the base address of the two strings and not the contents.
So, to compare the contents, you should use strcmp(). So your while loop will look something like this will look like this.
while(strcmp(var[i].casuality, "output") == 0)
You should use the function strcmp to compare string in C and loop like this :
while (strcmp(var[i].casuality,"output") != 0)
The condition in your loop is an affectation, not a comparaison between two string.

Counting of objects in linked list

I have a linked list and I need to count only some of records according some criteria. I have got this code.
int count_records() {
int number = 0;
RECORD *re;
char criteria[20];
for (re = first; re != NULL; re = re->next) {
criteria = (re->criteria);
if(criteria=="something")
number++;
}
return number; }
Structure of linked list is this:
typedef struct record {
char criteria[20];
struct record *next;
} RECORD;
Problem is that it gives me this error on line criteria = (re->criteria); Error: Invalid array assignment
Any ideas?
Thanks.
You have arrays of char´s. To copy or compare an array, you´ll need to copy/compare each element separately instead of using = and == on the whole array in one go.
And instead of making a loop, you can use strcpy and strcmp in this case:
strcpy(criteria, re->criteria);
if(!strcmp(criteria, "something"))
By the way, this is more C than C++. If you want a C++-like solution, see std::string (then you can work with = and == too)
Indeed, you can't copy arrays using =. Neither can you compare them using ==. They are quite strange, unfriendly things, which need a certain amount of care to use.
If you want to use arrays, you can use the C library to compare them:
if (std::strcmp(re->criteria, "something") == 0)
Since this is C++, you might prefer to use std::string to represent strings; then it's just
if (re->criteria == "something")
and you can copy it with = if you need to (although you don't need to here).
In C++ you can and should use std::string for handling strings. You can find the reference (here).
Just define criteria as std::string criteria; instead of char criteria[20];.
Don't forget to add #include <string> to the include section.
You need to use memcpy in order to copy arrays, but be careful using memcpy when exposed on a public interface. I think there was a recent security issue involving this...
I think that you may wish to pass in the criterion to your count_records() function. I'd render it like this:
int count_records(RECORD *first, const char *criterion) {
int count = 0;
RECORD *re;
for (re = first; re != NULL; re = re->next) {
/* add to count if the strings match */
if(!strcmp(criterion, re->criteria))
count++;
}
return count;
}
Note that it also doesn't rely on first being a global variable which makes the code more reliable and easier to understand.
If you want to copy a char array into another, you need to use strcpy. For example:
// will copy from array1 to array2
strcpy(array2, array1);
You can't directly do array1 = array2. Because in this case you would manipulate the adresses (char *) of the arrays and not their values.
In addition, in order to compare a char array to another, use strcmp instead:
if(strcmp(criteria, "something") == 0)
P.S.: Since you're using C++, std::string is your friend, which you can do assignment using = and comparison using ==.
Dealing with strings in C is a royal pain when you are starting out with the language. You can checkout an online tutorial to get you started.
In the mean time, you can change your code to:
for (re = first; re != NULL; re = re->next) {
if (strcmp(re->criteria, "something) == 0)
number++;
}
You don't need to copy re->criteria to a local variable.

Split char* to char * Array

I'm trying to split a char* to an array of char* in C.
I'm used to program in Java / PHP OO. I know several easy way to do that in these languages but in C... I'm totally lost. I often have segfault for hours x)
I'm using TinyXML and getting info from XML File.
Here's the struct where we find the array.
const int MAX_GATES = 64;
typedef struct {
char *name;
char *firstname;
char *date;
char *id;
char *gates[MAX_GATES];
} UserInfos;
And here's where I fill this struct :
UserInfos * infos = (UserInfos*)malloc(1024);
infos->firstname = (char*)malloc(256);
infos->name = (char*)malloc(128);
infos->id = (char*)malloc(128);
infos->date = (char*)malloc(128);
sprintf(infos->firstname, "%s", card->FirstChild("firstname")->FirstChild()->Value());
sprintf(infos->name, "%s", card->FirstChild("name")->FirstChild()->Value());
sprintf(infos->date, "%s", card->FirstChild("date")->FirstChild()->Value());
sprintf(infos->id, "%s", card->FirstChild("filename")->FirstChild()->Value());
////////////////////////
// Gates
char * gates = (char*) card->FirstChild("gates")->FirstChild()->Value();
//////////////////////////
The only problem is on 'gates'.
The input form XML looks like "gate1/gate2/gate3" or just blank sometimes.
I want gate1 to be in infos->gates[0] ; etc.
I want to be able to list the gates array afterwards..
I always have a segfault when I try.
Btw, I don't really now how to initialize this array of pointers. I always initialize all gates[i] to NULL but It seems that I've a segfault when I do
for(int i=0;i
Thanks for all.
It's OK when I've only pointers but when String(char*) / Arrays / Pointers are mixed.. I can't manage =P
I saw too that we can use something like
int *myArray = calloc(NbOfRows, NbOfRows*sizeof(int));
Why should we declare an array like that.. ? x)
Thanks!
The problem that people frequently have with XML is that they assume all the elements are available. That's not always safe. Thus this statement:
sprintf(infos->firstname, "%s", card->FirstChild("firstname")->FirstChild()->Value());
Isn't safe to do because you don't actually know if all of those
functions actually return valid objects. You really need something
like the following (which is not optimized for speed, as I don't
know the tinyXML structure name being returned at each point and thus
am not storing the results once and am rather calling each function
multiple times:
if (card->FirstChild("firstname") &&
card->FirstChild("firstname")->FirstChild()) {
sprintf(infos->firstname, "%s", card->FirstChild("firstname")->FirstChild()->Value());
}
And then, to protect against buffer overflows from the data you should
really be doing:
if (card->FirstChild("firstname") &&
card->FirstChild("firstname")->FirstChild()) {
infos->firstname[sizeof(infos->firstname)-1] = '\0';
snprintf(infos->firstname, sizeof(infos->firstname)-1, "%s", card->FirstChild("firstname")->FirstChild()->Value());
}
Don't you just love error handling?
As to your other question:
I saw too that we can use something like int *myArray =
calloc(NbOfRows, NbOfRows*sizeof(int)); Why should we declare an array
like that.. ? x)
calloc first initializes the resulting memory to 0, unlike malloc.
If you see above where I set the end of the buffer to '\0' (which is
actually 0), that's because malloc returns a buffer with potentially
random (non-zero) data in it. calloc will first set the entire buffer
to all 0s first, which can be generally safer.

Resources