I'm trying to separate the following string into three separate variables, i.e., a, b and c.:
" mov/1/1/1,0 STR{7}, r7"
each need to hold a different segment of the string, e.g:
a = "mov/1/1/1,0"
b = "STR{7}"
c = "r7"
There may be a space or also a tab between each command; this what makes this code part trickier.
I tried to use strtok, for the string manipulation, but it didn't work out.
char command[50] = " mov/1/1/1,0 STR{7}, r7";
char a[10], b[10], c[10];
char * ptr = strtok(command, "\t");
strcpy(a, ptr);
ptr = strtok(NULL, "\t");
strcpy(b, ptr);
ptr = strtok(NULL, ", ");
strcpy(c, ptr);
but this gets things really messy as the variables a, b and c get to hold more values than they should, which leads the program to crash.
Input may vary from:
" mov/1/1/1,0 STR{7}, r7"
"jsr /0,0 PRTSTR"
"mov/1/1/0,0 STRADD{5}, LASTCHAR {r3} "
in which the values of a,b and c change to different part of the given string.
I was told it is safer to use sscanf for that kind of manners than strtok, but I'm not sure why and how it could assist me.
I would be more than glad to hear your opinion!
This should do the trick :
sscanf(command, "%s,%s,%s", &a, &b, &c)
From scanf manpage, %s eats whitespaces, be them spaces or tabs :
s : Matches a sequence of non-white-space characters; the next pointer
must be a pointer to character array that is long enough to hold the
input sequence and the terminating null byte ('\0'), which is added
automatically. The input string stops at white space or at the
maximum field width, whichever occurs first.
As you might be knowing that you can use sscanf() the same way as scanf(), the difference is sscanf scans from string, while scanf from standard input.
In this problem you can specify scanf, with a set of characters to "always skip", as done in this link.
Since you have different set of constraints for scanning all the three strings, you can specify, using %*[^...], these constraints, before every %s inside sscanf().
I have reservations about using strtok(), but this code using it seems to do what you need. As I noted in a comment, the sample string "jsr /0,0 PRTSTR" throws a spanner in the works; it has a significant comma in the second field, whereas in the other two example strings, the comma in the second field is not significant. If you need to remove trailing commas, you can do that after the space-based splitting — as shown in this code. The second loop tests the zap_trailing_commas() function to ensure that it behaves under degenerate cases, zapping trailing commas but not underflowing the start of the buffer or anything horrid.
#include <stdio.h>
#include <string.h>
static void zap_trailing_commas(char *str)
{
size_t len = strlen(str);
while (len-- > 0 && str[len] == ',')
str[len] = '\0';
}
static void splitter(char *command)
{
char a[20], b[20], c[20];
char *ptr = strtok(command, " \t");
strcpy(a, ptr);
zap_trailing_commas(a);
ptr = strtok(NULL, " \t");
strcpy(b, ptr);
zap_trailing_commas(b);
ptr = strtok(NULL, " \t");
strcpy(c, ptr);
zap_trailing_commas(c);
printf("<<%s>> <<%s>> <<%s>>\n", a, b, c);
}
int main(void)
{
char data[][50] =
{
" mov/1/1/1,0 STR{7}, r7",
"jsr /0,0 PRTSTR",
"mov/1/1/0,0 STRADD{5}, LASTCHAR {r3} ",
};
for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); i++)
splitter(data[i]);
char commas[][10] = { "X,,,", "X,,", "X,", "X" };
for (size_t i = 0; i < sizeof(commas)/sizeof(commas[0]); i++)
{
printf("<<%s>> ", commas[i]);
zap_trailing_commas(&commas[i][1]);
printf("<<%s>>\n", commas[i]);
}
return 0;
}
Sample output:
<<mov/1/1/1,0>> <<STR{7}>> <<r7>>
<<jsr>> <</0,0>> <<PRTSTR>>
<<mov/1/1/0,0>> <<STRADD{5}>> <<LASTCHAR>>
<<X,,,>> <<X>>
<<X,,>> <<X>>
<<X,>> <<X>>
<<X>> <<X>>
I also tested a variant with commas in place of the X's and that left the single comma alone.
Related
I am having some trouble using system calls in C. I am trying to use read to read some input from stdin, then use strtok to load the values in an array, but I can't seem to do that right (I keep on getting segfault).
Here is the code I began with:
void read_input()
{
char* c;
read(0, c, 128);
printf("%s", c);
}
So, this works fine, and I can print the output. However, I have tried several things next and they haven't worked. I have tried:
Creating an array char arr[128], then using different variations of strcpy, strncpy, and memcpy to copy c into arr, but they haven't worked, and I get a segfault.
Actually, that's all I have tried. I am not sure how I am supposed to copy c into arr so I can use strtok. Can anyone explain?
Thanks
Edit:
Okay, this is my new code:
void
read_input()
{
char arr[129];
int r = read(0, arr, 129);
printf("%s", arr);
arr[r] = '\0';
char* pch;
pch = strtok(arr, " \n");
while(pch != NULL)
{
printf("%s\n", pch);
pch = strtok(NULL, " \n");
}
}
I am trying to read from stdin an input like "hi my name is john". This is the output I get from the printfs:
hi my name is john
�����hi
my
name
is
john
Why does the first token look like that? I noticed that if I dont add arr[r] = '\0', then "john" will look similar to "hi". Is there something I need to do for the first character, similar to the last?
You could directly go for this:
void read_input()
{
char arr[128];
read(0, arr, 128);
printf("%s", arr);
}
Or the dynamic memory allocation route:
char *arr = malloc((sizeof(*arr) * 128) + 1);
if (arr == NULL)
{
// handle error
}
int r = read(0, arr, 128);
arr[r] = '\0';
printf("%s", arr);
Your second program doesn't work because you put the NUL terminator after the the call to printf. You need to do it before printf:
char arr[129]; // 128 + 1 for the NUL terminator
int r = read(0, arr, 128);
arr[r] = '\0';
printf("%s\n", arr); // \n for better readbility of the output
You also need one more byte for the NUL terminator, hence the 129.
printf with the %s format specifier prints NUL terminated strings. As you don't put the NUL before printf, latter displays all characters until it encounters a NUL hence the output you get:
hi my name is john
�����
...
This output may vary, it depends on the previous content of the arr buffer.
The first version is wrong because the c pointer is not initialized, it points nowhere. The program may appear to work correctly in this case, but this is so called "undefined behaviour", google that and keep in mind that "undefined behaviour" includes "apparently working fine".
I am trying to save one character and 2 strings into variables.
I use sscanf to read strings with the following form :
N "OldName" "NewName"
What I want : char character = 'N' , char* old_name = "OldName" , char* new_name = "NewName" .
This is how I am trying to do it :
sscanf(mystring,"%c %s %s",&character,old_name,new_name);
printf("%c %s %s",character,old_name,new_name);
The problem is , my problem stops working without any outputs .
(I want to ignore the quotation marks too and save only its content)
When you do
char* new_name = "NewName";
you make the pointer new_name point to the read-only string array containing the constant string literal. The array contains exactly 8 characters (the letters of the string plus the terminator).
First of all, using that pointer as a destination for scanf will cause scanf to write to the read-only array, which leads to undefined behavior. And if you give a string longer than 7 character then scanf will also attempt to write out of bounds, again leading to undefined behavior.
The simple solution is to use actual arrays, and not pointers, and to also tell scanf to not read more than can fit in the arrays. Like this:
char old_name[64]; // Space for 63 characters plus string terminator
char new_name[64];
sscanf(mystring,"%c %63s %63s",&character,old_name,new_name);
To skip the quotation marks you have a couple of choices: Either use pointers and pointer arithmetic to skip the leading quote, and then set the string terminator at the place of the last quote to "remove" it. Another solution is to move the string to overwrite the leading quote, and then do as the previous solution to remove the last quote.
Or you could rely on the limited pattern-matching capabilities of scanf (and family):
sscanf(mystring,"%c \"%63s\" \"%63s\"",&character,old_name,new_name);
Note that the above sscanf call will work iff the string actually includes the quotes.
Second note: As said in the comment by Cool Guy, the above won't actually work since scanf is greedy. It will read until the end of the file/string or a white-space, so it won't actually stop reading at the closing double quote. The only working solution using scanf and family is the one below.
Also note that scanf and family, when reading string using "%s" stops reading on white-space, so if the string is "New Name" then it won't work either. If this is the case, then you either need to manually parse the string, or use the odd "%[" format, something like
sscanf(mystring,"%c \"%63[^\"]\" \"%63[^\"]\"",&character,old_name,new_name);
You must allocate space for your strings, e.g:
char* old_name = malloc(128);
char* new_name = malloc(128);
Or using arrays
char old_name[128] = {0};
char new_name[128] = {0};
In case of malloc you also have to free the space before the end of your program.
free(old_name);
free(new_name);
Updated:...
The other answers provide good methods of creating memory as well as how to read the example input into buffers. There are two additional items that may help:
1) You expressed that you want to ignore the quotation marks too.
2) Reading first & last names when separated with space. (example input is not)
As #Joachim points out, because scanf and family stop scanning on a space with the %s format specifier, a name that includes a space such as "firstname lastname" will not be read in completely. There are several ways to address this. Here are two:
Method 1: tokenizing your input.
Tokenizing a string breaks it into sections separated by delimiters. Your string input examples for instance are separated by at least 3 usable delimiters: space: " ", double quote: ", and newline: \n characters. fgets() and strtok() can be used to read in the desired content while at the same time strip off any undesired characters. If done correctly, this method can preserve the content (even spaces) while removing delimiters such as ". A very simple example of the concept below includes the following steps:
1) reading stdin into a line buffer with fgets(...)
2) parse the input using strtok(...).
Note: This is an illustrative, bare-bones implementation, sequentially coded to match your input examples (with spaces) and includes none of the error checking/handling that would normally be included.
int main(void)
{
char line[128];
char delim[] = {"\n\""};//parse using only newline and double quote
char *tok;
char letter;
char old_name[64]; // Space for 63 characters plus string terminator
char new_name[64];
fgets(line, 128, stdin);
tok = strtok(line, delim); //consume 1st " and get token 1
if(tok) letter = tok[0]; //assign letter
tok = strtok(NULL, delim); //consume 2nd " and get token 2
if(tok) strcpy(old_name, tok); //copy tok to old name
tok = strtok(NULL, delim); //consume 3rd " throw away token 3
tok = strtok(NULL, delim); //consume 4th " and get token 4
if(tok) strcpy(new_name, tok); //copy tok to new name
printf("%c %s %s\n", letter, old_name, new_name);
return 0;
}
Note: as written, this example (as do most strtok(...) implementations) require very narrowly defined input. In this case input must be no longer than 127 characters, comprised of a single character followed by space(s) then a double quoted string followed by more space(s) then another double quoted string, as defined by your example:
N "OldName" "NewName"
The following input will also work in the above example:
N "old name" "new name"
N "old name" "new name"
Note also about this example, some consider strtok() broken, while others suggest avoiding its use. I suggest using it sparingly, and only in single threaded applications.
Method 2: walking the string.
A C string is just an array of char terminated with a NULL character. By selectively copying some characters into another string, while bypassing the one you do not want (such as the "), you can effectively strip unwanted characters from your input. Here is an example function that will do this:
char * strip_ch(char *str, char ch)
{
char *from, *to;
char *dup = strdup(str);//make a copy of input
if(dup)
{
from = to = dup;//set working pointers equal to pointer to input
for (from; *from != '\0'; from++)//walk through input string
{
*to = *from;//set destination pointer to original pointer
if (*to != ch) to++;//test - increment only if not char to strip
//otherwise, leave it so next char will replace
}
*to = '\0';//replace the NULL terminator
strcpy(str, dup);
free(dup);
}
return str;
}
Example use case:
int main(void)
{
char line[128] = {"start"};
while(strstr(line, "quit") == NULL)
{
printf("Enter string (\"quit\" to leave) and hit <ENTER>:");
fgets(line, 128, stdin);
sprintf(line, "%s\n", strip_ch(line, '"'));
printf("%s", line);
}
return 0;
}
This question already has answers here:
scanning a string to hex char array
(3 answers)
Closed 8 years ago.
Here is my Code :
char a[18], b[18];
char oper, clear;
char *test;
init_8051();
test="0x1234567890123456 + 0x1234567890123456\0";
printf("Please enter an equation: %s \n",test );
sscanf(test,"0x%s %c 0x%s",a,&oper,b);
printf(" a= %s \n" ,a);
printf(" oper= %s \n" ,oper);
printf(" b= %s \n" ,b);
I want to accept to hex numbers with an operation as a string and to be able to seperate those 2 numbers into 2 separate char arrays but it doesnt wanna work, here is the output of the following code :
Please enter an equation: 0x1234567890123456 + 0x1234567890123456
a= 1234567890123456
oper= Ò
b= 1234567890123456
As you can see the operation is not recognized and also i have to use spaces which i wish i didnt have to use i wish it to be in the format of 0x1234567890123456+0x1234567890123456
with no spaces between the plus and the number.
Thanks
From the sscanf manual
s Matches a sequence of non-white-space characters; the next pointer must be a pointer to character array that is long enough to hold the input sequence and the
terminating null byte ('\0'), which is added automatically. The input string stops at white space or at the maximum field width, whichever occurs first.
It means that %s consumes the + and the rest of the characters, leaving b and oper uninitalized, abd overflowing a since it only has space for 18 characters.
So when the input string is lacking the space after the first operand, sscanf will continue reading until it finds a whitespace character. Hence when the string does not contain the separating space between the operands and the operator, sscanf consumes all the input.
I'll append here a different approach to your problems solution
We copy the string, this is required by strtok you can't pass an inmutable string, there are plenty of methods to copy this string, you just have to pick the appropriate one for your case
input = strdup("0x1234567890123456 + 0x1234567890123456\0");
Now, we use strpbrk to find the operator
pointer = strpbrk(input, "+-*/" /* here go the operators */);
if (pointer != NULL)
oper = *pointer; /* this will contain the operator ascii value */
Create a string containing the operator as a delimiter
operstr[0] = oper;
operstr[1] = '\0'; /* strings must be null terminated */
Now, we use strtok to tokenize the string, and find the operands
pointer = strtok(input, operstr);
if (pointer != NULL)
fprintf(stderr, "first operand: %s\n", pointer); /* you can copy this string if you need to */
printf("Operator: %s \n", operstr);
Second call to strtok needs NULL first argument
pointer = strtok(NULL, operstr);
if (pointer != NULL)
fprintf(stderr, "second operand: %s\n", pointer); /* you can copy this string if you need to */
And finally free our copy of the input string.
free(input);
It is better to use strtok_r the reentrant version. But for now you could test my suggestions and may be, it is what you need.
Even though this will work for this particular situation it is not the preferred way of doing this kind of thing, you can try writing a parser and use Reverse Polish Notation, or you can try with a lexical analyzer and a parser generator like flex and bison.
My previous answer was downvoted, and didn't address all of OP's requirements, so I have rewritten this answer.
OP wants flexible input, either spaces or no spaces. I suggest not using sscanf() but the methods below. First the program finds a valid operator by using strcspn(), then breaks the string using strtok() on operators and whitespace. But using strtok() on a string literal is UB so I copy the "equation" to another string first.
I also corrected the printf() field spec for the operator, and made a and b different - it's always a bad idea using the same values for different variables in an example.
#include <stdio.h>
#include <string.h>
#define OPERATORS "+-*/"
#define DELIMS " \t\n" OPERATORS
int parse (char *test)
// return 1 if parsed successfully
{
char a[50], b[50];
char oper;
char *ptr;
int opind;
opind = strcspn (test, OPERATORS); // find operator
if (opind < 1) return 0; // fail
oper = test[opind]; // collect operator
ptr = strtok (test, DELIMS); // find a
if (ptr == NULL) return 0; // fail
strcpy (a, ptr); // collect 1st arg
ptr = strtok (NULL, DELIMS); // find b
if (ptr == NULL) return 0; // fail
strcpy (b, ptr); // collect 2nd arg
printf(" a %s \n" ,a);
printf(" oper %c \n" ,oper); // corrected format
printf(" b %s \n" ,b);
return 1;
}
int main (void)
{
char test[100];
strcpy (test, "0x123456789ABCDEF0+0xFEDCBA9876543210");
if (!parse (test))
printf("Failed\n");
printf("\n");
strcpy (test, "0x123456789ABCDEF0 + 0xFEDCBA9876543210");
if (!parse (test))
printf("Failed\n");
return 0;
}
Program output
a 0x123456789ABCDEF0
oper +
b 0xFEDCBA9876543210
a 0x123456789ABCDEF0
oper +
b 0xFEDCBA9876543210
I have a string that has # and ! symbols throughout it. My task is to separate all the content in between those symbols into new strings. I don't know how many symbols there are going to be.
Can I use the scanf function to separate them into strings
I was thinking something like this:
input string: dawddwamars#dawdjiawjd!fjejafi!djiajoa#jdawijd#
char s1[20], s2[20], s3[20], s4[20], s5[20];
scanf("%s[^!][^#]%s[^!][^#]%s[^!][^#]%s[^!][^#]%s[^!][^#]", s1, s2, s3, s4, s5);
Would this work? Or does someone have a method that is better.
I need to separate the string into substrings because i have to search for the longest common substring in those new strings.
To get you started, here is some non-flexible code concerning number of substrings you will get out of the input string (strings array). As already mentioned, use strsep() (since strtok() is obsoleted by it, see man strtok).
#include <stdio.h>
#include <string.h>
int main() {
char* str;
char* token;
char* temp;
char strings[10][20];
int i = 0;
str = strdup("dawddwamars#dawdjiawjd!fjejafi!djiajoa#jdawijd#");
printf("%s\n", str);
while ((token = strsep(&str, "#")) != NULL) {
temp = strdup(token);
while ((token = strsep(&temp, "!")) != NULL) {
printf("%s\n", token);
strcpy(strings[i], token);
i++;
}
}
}
If you must use scanf()
#define Fmt1 "%19[^#!]"
#define Sep1 "%*[#!]"
char s1[20], s2[20], s3[20], s4[20], s5[20];
int count = scanf(" " Fmt1 Sep1 Fmt1 Sep1 Fmt1 Sep1 Fmt1 Sep1 Fmt1,
s1, s2, s3, s4, s5);
// count represents the number of successfully scanned strings.
// Expected range 0 to 5 and EOF.
scanf() has a lot of features that just aren't needed here.
To be more efficient, I would probably use strtok(), which seems ideal for this task.
Alternatively, I might just write C code to find the next # or ! using strchr() or a simple loop, and then just extract the tokens myself.
I'm having a lot of trouble figuring this out. I have a C string, and I want to remove the first part of it. Let's say its: "Food,Amount,Calories". I want to copy out each one of those values, but not the commas. I find the comma, and return the position of the comma to my method. Then I use
strncpy(aLine.field[i], theLine, end);
To copy "theLine" to my array at position "i", with only the first "end" characters (for the first time, "end" would be 4, because that is where the first comma is). But then, because it's in a Loop, I want to remove "Food," from the array, and do the process over again. However, I cannot see how I can remove the first part (or move the array pointer forward?) and keep the rest of it. Any help would be useful!
What you need is to chop off strings with comma as your delimiter.
You need strtok to do this. Here's an example code for you:
int main (int argc, const char * argv[]) {
char *s = "asdf,1234,qwer";
char str[15];
strcpy(str, s);
printf("\nstr: %s", str);
char *tok = strtok(str, ",");
printf("\ntok: %s", tok);
tok = strtok(NULL, ",");
printf("\ntok: %s", tok);
tok = strtok(NULL, ",");
printf("\ntok: %s", tok);
return 0;
}
This will give you the following output:
str: asdf,1234,qwer
tok: asdf
tok: 1234
tok: qwer
If you have to keep the original string, then strtok. If not, you can replace each separator with '\0', and use the obtained strings directly:
char s_RO[] = "abc,123,xxxx", *s = s_RO;
while (s){
char* old_str = s;
s = strchr(s, ',');
if (s){
*s = '\0';
s++;
};
printf("found string %s\n", old_str);
};
The function you might want to use is strtok()
Here is a nice example - http://www.cplusplus.com/reference/clibrary/cstring/strtok/
Personally, I would use strtok().
I would not recommend removing extracted tokens from the string. Removing part of a string requires copying the remaining characters, which is not very efficient.
Instead, you should keep track of your positions and just copy the sections you want to the new string.
But, again, I would use strtok().
if you know where the comma is, you can just keep reading the string from that point on.
for example
void readTheString(const char *theLine)
{
const char *wordStart = theLine;
const char *wordEnd = theLine;
int i = 0;
while (*wordStart) // while we haven't reached the null termination character
{
while (*wordEnd != ',')
wordEnd++;
// ... copy the substring ranging from wordStart to wordEnd
wordStart = ++wordEnd; // start the next word
}
}
or something like that.
the null termination check is probably wrong, unless the string also ends with a ','... but you get the idea.
anyway, using strtok would probably be a better idea.