Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #112351 > unrolled thread
| Started by | DFS <nospam@dfs.com> |
|---|---|
| First post | 2017-06-16 13:33 -0400 |
| Last post | 2017-06-17 18:25 -0400 |
| Articles | 20 on this page of 21 — 9 participants |
Back to article view | Back to comp.lang.c
Splitting strings into arrays with sscanf DFS <nospam@dfs.com> - 2017-06-16 13:33 -0400
Re: Splitting strings into arrays with sscanf scott@slp53.sl.home (Scott Lurndal) - 2017-06-16 17:46 +0000
Re: Splitting strings into arrays with sscanf DFS <nospam@dfs.com> - 2017-06-16 14:02 -0400
Re: Splitting strings into arrays with sscanf scott@slp53.sl.home (Scott Lurndal) - 2017-06-16 18:15 +0000
Re: Splitting strings into arrays with sscanf Keith Thompson <kst-u@mib.org> - 2017-06-16 12:28 -0700
Re: Splitting strings into arrays with sscanf Keith Thompson <kst-u@mib.org> - 2017-06-16 12:20 -0700
Re: Splitting strings into arrays with sscanf Lew Pitcher <lew.pitcher@digitalfreehold.ca> - 2017-06-16 13:47 -0400
Re: Splitting strings into arrays with sscanf scott@slp53.sl.home (Scott Lurndal) - 2017-06-16 18:01 +0000
Re: Splitting strings into arrays with sscanf Barry Schwarz <schwarzb@dqel.com> - 2017-06-16 12:03 -0700
Re: Splitting strings into arrays with sscanf "James R. Kuyper" <jameskuyper@verizon.net> - 2017-06-16 15:33 -0400
Re: Splitting strings into arrays with sscanf Keith Thompson <kst-u@mib.org> - 2017-06-16 13:16 -0700
Re: Splitting strings into arrays with sscanf Keith Thompson <kst-u@mib.org> - 2017-06-16 12:31 -0700
Re: Splitting strings into arrays with sscanf "James R. Kuyper" <jameskuyper@verizon.net> - 2017-06-16 15:45 -0400
Re: Splitting strings into arrays with sscanf "James R. Kuyper" <jameskuyper@verizon.net> - 2017-06-16 14:07 -0400
Re: Splitting strings into arrays with sscanf "James R. Kuyper" <jameskuyper@verizon.net> - 2017-06-16 14:22 -0400
Re: Splitting strings into arrays with sscanf DFS <nospam@dfs.com> - 2017-06-16 15:36 -0400
Re: Splitting strings into arrays with sscanf "James R. Kuyper" <jameskuyper@verizon.net> - 2017-06-16 17:17 -0400
Re: Splitting strings into arrays with sscanf Barry Schwarz <schwarzb@dqel.com> - 2017-06-16 12:06 -0700
Re: Splitting strings into arrays with sscanf Real Troll <Real.Troll@Trolls.com> - 2017-06-16 17:30 -0400
Re: Splitting strings into arrays with sscanf ike@iceland.freeshell.org - 2017-06-17 16:49 +0000
Re: Splitting strings into arrays with sscanf Richard Damon <Richard@Damon-Family.org> - 2017-06-17 18:25 -0400
Page 1 of 2 [1] 2 Next page →
| From | DFS <nospam@dfs.com> |
|---|---|
| Date | 2017-06-16 13:33 -0400 |
| Subject | Splitting strings into arrays with sscanf |
| Message-ID | <oi14f9$edg$3@dont-email.me> |
How do you use sscanf to split a line into pieces and add each piece to
an array?
#include <stdio.h>
#include <string.h>
int main (int argc, char *argv[])
{
char line[50] = "";
char lbls[4] = "";
char data[10][4] = "";
strcpy(line,"Name,Quarter,Product,Sales");
sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
printf("%s\n",lbls[2]);
strcpy(line,"Jim,Q1,computer,100");
sscanf(line, "%s,%s,%s,%s", data[0][0],data[0][1],data[0][2],data[0][3]);
return 0;
}
tcc throws an error that doesn't make sense:
Line 7: error: ',' expected (got ";")
[toc] | [next] | [standalone]
| From | scott@slp53.sl.home (Scott Lurndal) |
|---|---|
| Date | 2017-06-16 17:46 +0000 |
| Message-ID | <kQU0B.45$9D5.31@fx12.iad> |
| In reply to | #112351 |
DFS <nospam@dfs.com> writes:
>How do you use sscanf to split a line into pieces and add each piece to
>an array?
>
>
>#include <stdio.h>
>#include <string.h>
>int main (int argc, char *argv[])
>{
> char line[50] = "";
> char lbls[4] = "";
> char data[10][4] = "";
The initializations don't match the declarations, and
while lines 5 & 6 don't get errors, they won't provide you
with the results you desire. Instead, lose the initializers
and use memset to initialize the vectors to your desired value(s).
e.g.
char data[10][4];
memset(data, 0, sizeof(data));
[toc] | [prev] | [next] | [standalone]
| From | DFS <nospam@dfs.com> |
|---|---|
| Date | 2017-06-16 14:02 -0400 |
| Message-ID | <oi168c$l43$2@dont-email.me> |
| In reply to | #112352 |
On 6/16/2017 1:46 PM, Scott Lurndal wrote:
> DFS <nospam@dfs.com> writes:
>> How do you use sscanf to split a line into pieces and add each piece to
>> an array?
>>
>>
>> #include <stdio.h>
>> #include <string.h>
>> int main (int argc, char *argv[])
>> {
>> char line[50] = "";
>> char lbls[4] = "";
>> char data[10][4] = "";
>
> The initializations don't match the declarations, and
> while lines 5 & 6 don't get errors, they won't provide you
> with the results you desire. Instead, lose the initializers
> and use memset to initialize the vectors to your desired value(s).
>
> e.g.
>
> char data[10][4];
>
> memset(data, 0, sizeof(data));
Done, and then it compiles, but when run it crashes on line 14:
printf("%s\n",lbls[2]);
#include <stdio.h>
#include <string.h>
int main (int argc, char *argv[])
{
char line[50] ;
char lbls[4] ;
char data[10][4] ;
memset(lbls, 0, sizeof(lbls));
memset(data, 0, sizeof(data));
strcpy(line,"Name,Quarter,Product,Sales");
sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
printf("%s\n",lbls[2]);
strcpy(line,"Jim,Q1,computer,100");
sscanf(line, "%s,%s,%s,%s", data[0][0],data[0][1],data[0][2],data[0][3]);
printf("%s\n",data[3]);
return 0;
}
[toc] | [prev] | [next] | [standalone]
| From | scott@slp53.sl.home (Scott Lurndal) |
|---|---|
| Date | 2017-06-16 18:15 +0000 |
| Message-ID | <WeV0B.2768$AG1.49@fx42.iad> |
| In reply to | #112355 |
DFS <nospam@dfs.com> writes:
>On 6/16/2017 1:46 PM, Scott Lurndal wrote:
>> DFS <nospam@dfs.com> writes:
>>> How do you use sscanf to split a line into pieces and add each piece to
>>> an array?
>>>
>>>
>>> #include <stdio.h>
>>> #include <string.h>
>>> int main (int argc, char *argv[])
>>> {
>>> char line[50] = "";
>>> char lbls[4] = "";
>>> char data[10][4] = "";
>>
>> The initializations don't match the declarations, and
>> while lines 5 & 6 don't get errors, they won't provide you
>> with the results you desire. Instead, lose the initializers
>> and use memset to initialize the vectors to your desired value(s).
>>
>> e.g.
>>
>> char data[10][4];
>>
>> memset(data, 0, sizeof(data));
>
>
>Done, and then it compiles, but when run it crashes on line 14:
In C, a string is an array of characters terminated by
an ASCII (or UTF-8) NUL byte.
>
>printf("%s\n",lbls[2]);
>
>
>
>#include <stdio.h>
>#include <string.h>
>int main (int argc, char *argv[])
>{
> char line[50] ;
> char lbls[4] ;
This declaration reserves four bytes of memory (four 1-byte characters).
> char data[10][4] ;
>
> memset(lbls, 0, sizeof(lbls));
This sets all four bytes to zero.
> memset(data, 0, sizeof(data));
>
> strcpy(line,"Name,Quarter,Product,Sales");
> sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
This line (the scanf) is the root of your problem. The arguments to
scanf when the format character is '%s' must be the first address of a
character vector sized sufficiently to hold the value being scanned.
Passing lbls[0] passes the value zero as the argument. When sscanf
processes the first '%s', it will take that zero and use it as the
address of the first byte in which to store the matching data. Since
the value zero maps to the NULL pointer on pretty much all current
systems, you'll get a segmentation violation when attempting to store
the result.
If you had, instead, written:
sscanf(line, "%s,%s,%s,%s", &lbls[0], &lbls[1], &lbls[2], &lbls[3]);
Then the correct 'char *' arguments would have been passed to the sscanf
function. However, there is only one byte of storage available at each
of those pointers, so you'll end up corrupting your stack.
Something like:
char name[100];
char quarter[100];
char product[100];
char sales[100];
sscanf(line, "%s,%s,%s,%s", name, quarter, product, sales);
will work much better (although there are still two problems with it:
1) If any field is greater than 99 bytes, you'll corrupt memory
(you could use %99s instead of %s to prevent this)
and 2) '%s' matches up to the next whitespace, so the first %s will
capture the entire string you specified including the commas.
> printf("%s\n",lbls[2]);
>
> strcpy(line,"Jim,Q1,computer,100");
> sscanf(line, "%s,%s,%s,%s", data[0][0],data[0][1],data[0][2],data[0][3]);
> printf("%s\n",data[3]);
>
> return 0;
>}
>
>
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-06-16 12:28 -0700 |
| Message-ID | <lny3srag0n.fsf@kst-u.example.com> |
| In reply to | #112357 |
scott@slp53.sl.home (Scott Lurndal) writes:
> DFS <nospam@dfs.com> writes:
[...]
>>Done, and then it compiles, but when run it crashes on line 14:
>
> In C, a string is an array of characters terminated by
> an ASCII (or UTF-8) NUL byte.
C doesn't mention ASCII (other than a couple of non-normative
footnotes). A string is terminated by a null character, i.e., a
character with the value zero ('\0').
[...]
>> char line[50] ;
>> char lbls[4] ;
>
> This declaration reserves four bytes of memory (four 1-byte characters).
>
>> char data[10][4] ;
>>
>> memset(lbls, 0, sizeof(lbls));
>
> This sets all four bytes to zero.
Which means that lbls contains a string of length 0. The original code:
char lbls[4] = "";
did exactly the same thing.
>> memset(data, 0, sizeof(data));
>>
>> strcpy(line,"Name,Quarter,Product,Sales");
>> sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
>
> This line (the scanf) is the root of your problem. The arguments to
> scanf when the format character is '%s' must be the first address of a
> character vector sized sufficiently to hold the value being scanned.
>
> Passing lbls[0] passes the value zero as the argument. When sscanf
> processes the first '%s', it will take that zero and use it as the
> address of the first byte in which to store the matching data. Since
> the value zero maps to the NULL pointer on pretty much all current
> systems, you'll get a segmentation violation when attempting to store
> the result.
More simply, "%s" requires an argument of type char*, but it's being
given an argument of type int (lbls[0] is of type char, which promotes
to int). The behavior is undefined.
> If you had, instead, written:
>
> sscanf(line, "%s,%s,%s,%s", &lbls[0], &lbls[1], &lbls[2], &lbls[3]);
>
> Then the correct 'char *' arguments would have been passed to the sscanf
> function. However, there is only one byte of storage available at each
> of those pointers, so you'll end up corrupting your stack.
&lbls[0] points to the beginning of a 4-byte object, but the other
arguments point to overlapping locations within that object. If you wrote:
sscanf(line, "%s", &lbls[0]);
it would be fine as long as the scanned data is no more than 3 bytes
long (an additional byte is needed for the trailing '\0').
[...]
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-06-16 12:20 -0700 |
| Message-ID | <ln37azbuz8.fsf@kst-u.example.com> |
| In reply to | #112352 |
scott@slp53.sl.home (Scott Lurndal) writes:
> DFS <nospam@dfs.com> writes:
>>How do you use sscanf to split a line into pieces and add each piece to
>>an array?
>>
>>#include <stdio.h>
>>#include <string.h>
>>int main (int argc, char *argv[])
>>{
>> char line[50] = "";
>> char lbls[4] = "";
>> char data[10][4] = "";
>
> The initializations don't match the declarations, and
> while lines 5 & 6 don't get errors, they won't provide you
> with the results you desire. Instead, lose the initializers
> and use memset to initialize the vectors to your desired value(s).
>
> e.g.
>
> char data[10][4];
>
> memset(data, 0, sizeof(data));
The initializers for `line` and `lbls` are perfectly valid. Only the
initialization for `data` is incorrect. One way of fixing it is:
char line[50] = "";
char lbls[4] = "";
char data[10][4] = { "" }; /* remaining elements are implicit */
or even:
char line[50] = { 0 };
char lbls[4] = { 0 };
char data[10][4] = { 0 };
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Lew Pitcher <lew.pitcher@digitalfreehold.ca> |
|---|---|
| Date | 2017-06-16 13:47 -0400 |
| Message-ID | <oi15e9$g8h$1@dont-email.me> |
| In reply to | #112351 |
DFS wrote:
> How do you use sscanf to split a line into pieces and add each piece to
> an array?
>
>
> #include <stdio.h>
> #include <string.h>
> int main (int argc, char *argv[])
> {
> char line[50] = "";
> char lbls[4] = "";
> char data[10][4] = "";
>
> strcpy(line,"Name,Quarter,Product,Sales");
> sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
> printf("%s\n",lbls[2]);
>
> strcpy(line,"Jim,Q1,computer,100");
> sscanf(line, "%s,%s,%s,%s", data[0][0],data[0][1],data[0][2],data[0][3]);
>
> return 0;
> }
>
>
> tcc throws an error that doesn't make sense:
> Line 7: error: ',' expected (got ";")
Line 7 looks to be...
char data[10][4] = "";
which describes an array of 10 elements, each containing an array of 4
characters. However, your initialization only consists of a single array of
1 character (a \0).
Apparently, tcc is expecting an initialization that looks something like
char data[10][4] = {"","","","","","","","","",""};
--
Lew Pitcher
"In Skills, We Trust"
PGP public key available upon request
[toc] | [prev] | [next] | [standalone]
| From | scott@slp53.sl.home (Scott Lurndal) |
|---|---|
| Date | 2017-06-16 18:01 +0000 |
| Message-ID | <F1V0B.2767$AG1.2130@fx42.iad> |
| In reply to | #112353 |
Lew Pitcher <lew.pitcher@digitalfreehold.ca> writes:
>DFS wrote:
>
>> How do you use sscanf to split a line into pieces and add each piece to
>> an array?
>>
>>
>> #include <stdio.h>
>> #include <string.h>
>> int main (int argc, char *argv[])
>> {
>> char line[50] = "";
>> char lbls[4] = "";
>> char data[10][4] = "";
>>
>> strcpy(line,"Name,Quarter,Product,Sales");
>> sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
>> printf("%s\n",lbls[2]);
>>
>> strcpy(line,"Jim,Q1,computer,100");
>> sscanf(line, "%s,%s,%s,%s", data[0][0],data[0][1],data[0][2],data[0][3]);
>>
>> return 0;
>> }
>>
>>
>> tcc throws an error that doesn't make sense:
>> Line 7: error: ',' expected (got ";")
>
>Line 7 looks to be...
> char data[10][4] = "";
>which describes an array of 10 elements, each containing an array of 4
>characters. However, your initialization only consists of a single array of
>1 character (a \0).
>
>Apparently, tcc is expecting an initialization that looks something like
> char data[10][4] = {"","","","","","","","","",""};
As "" is of type 'char *', the values assigned to the array will likely
not be as expected (i.e. either the high-byte (big-endian systems) or the
low-byte (little-endian systems) of the address to the empty string will
be stored in data, rather than the expected single NUL byte at column 0
of each of the ten rows).
Technically, the correct initializer looks more like:
char data[10][4] = {
{ 0, 1, 2, 3 }, /* row 0 */
{ 4, 5, 6, 7 }, /* row 1 */
...
{ 36, 37, 38, 39 }, /* row 9 */
};
or
char data[10][4] = {
{ 'a', 'b', 'c', 'd', }, /* row 0 */
...
{ 'A', 'B', 'C', 'D', }, /* row 9 */
};
[toc] | [prev] | [next] | [standalone]
| From | Barry Schwarz <schwarzb@dqel.com> |
|---|---|
| Date | 2017-06-16 12:03 -0700 |
| Message-ID | <7oa8kc99v10gnaf66jtumkl1t2q1tqmnh8@4ax.com> |
| In reply to | #112354 |
On Fri, 16 Jun 2017 18:01:09 GMT, scott@slp53.sl.home (Scott Lurndal)
wrote:
>Lew Pitcher <lew.pitcher@digitalfreehold.ca> writes:
>>DFS wrote:
>>
>>> How do you use sscanf to split a line into pieces and add each piece to
>>> an array?
>>>
>>>
>>> #include <stdio.h>
>>> #include <string.h>
>>> int main (int argc, char *argv[])
>>> {
>>> char line[50] = "";
>>> char lbls[4] = "";
>>> char data[10][4] = "";
>>>
>>> strcpy(line,"Name,Quarter,Product,Sales");
>>> sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
>>> printf("%s\n",lbls[2]);
>>>
>>> strcpy(line,"Jim,Q1,computer,100");
>>> sscanf(line, "%s,%s,%s,%s", data[0][0],data[0][1],data[0][2],data[0][3]);
>>>
>>> return 0;
>>> }
>>>
>>>
>>> tcc throws an error that doesn't make sense:
>>> Line 7: error: ',' expected (got ";")
>>
>>Line 7 looks to be...
>> char data[10][4] = "";
>>which describes an array of 10 elements, each containing an array of 4
>>characters. However, your initialization only consists of a single array of
>>1 character (a \0).
>>
>>Apparently, tcc is expecting an initialization that looks something like
>> char data[10][4] = {"","","","","","","","","",""};
>
> As "" is of type 'char *', the values assigned to the array will likely
>not be as expected (i.e. either the high-byte (big-endian systems) or the
>low-byte (little-endian systems) of the address to the empty string will
>be stored in data, rather than the expected single NUL byte at column 0
>of each of the ten rows).
When a string literal is used to initialize an array, the value of the
literal is placed in the array, not the address of the literal.
--
Remove del for email
[toc] | [prev] | [next] | [standalone]
| From | "James R. Kuyper" <jameskuyper@verizon.net> |
|---|---|
| Date | 2017-06-16 15:33 -0400 |
| Message-ID | <3adc8c75-0602-3641-f9b5-202538bddd4b@verizon.net> |
| In reply to | #112361 |
On 06/16/2017 03:03 PM, Barry Schwarz wrote:
> On Fri, 16 Jun 2017 18:01:09 GMT, scott@slp53.sl.home (Scott Lurndal)
> wrote:
>
>> Lew Pitcher <lew.pitcher@digitalfreehold.ca> writes:
...
>>> Apparently, tcc is expecting an initialization that looks something like
>>> char data[10][4] = {"","","","","","","","","",""};
>>
>> As "" is of type 'char *', the values assigned to the array will likely
>> not be as expected (i.e. either the high-byte (big-endian systems) or the
>> low-byte (little-endian systems) of the address to the empty string will
>> be stored in data, rather than the expected single NUL byte at column 0
>> of each of the ten rows).
>
> When a string literal is used to initialize an array, the value of the
> literal is placed in the array, not the address of the literal.
char *words[] = {"Are", "you", "sure", "of", "that?"};
What you're saying is close to being correct, it just needs a slight
change to the wording.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-06-16 13:16 -0700 |
| Message-ID | <lnd1a3k7r8.fsf@kst-u.example.com> |
| In reply to | #112370 |
"James R. Kuyper" <jameskuyper@verizon.net> writes:
> On 06/16/2017 03:03 PM, Barry Schwarz wrote:
>> On Fri, 16 Jun 2017 18:01:09 GMT, scott@slp53.sl.home (Scott Lurndal)
>> wrote:
>>> Lew Pitcher <lew.pitcher@digitalfreehold.ca> writes:
> ...
>>>> Apparently, tcc is expecting an initialization that looks something like
>>>> char data[10][4] = {"","","","","","","","","",""};
>>>
>>> As "" is of type 'char *', the values assigned to the array will likely
>>> not be as expected (i.e. either the high-byte (big-endian systems) or the
>>> low-byte (little-endian systems) of the address to the empty string will
>>> be stored in data, rather than the expected single NUL byte at column 0
>>> of each of the ten rows).
>>
>> When a string literal is used to initialize an array, the value of the
>> literal is placed in the array, not the address of the literal.
>
> char *words[] = {"Are", "you", "sure", "of", "that?"};
The string literal "Are" is used to initialize words[0], which is a
char*, not an array. The array is initialized by the brace-enclosed
initializer as a whole, which *contains* several string literals.
> What you're saying is close to being correct, it just needs a slight
> change to the wording.
Actually his wording is very close to what the Standard says.
C11 6.3.2.1p3 (emphasis added):
Except when it is the operand of the sizeof operator, or the
unary & operator, **or is a string literal used to initialize
an array**, an expression that has type "array of type" is
converted to an expression with type "pointer to type" that
points to the initial element of the array object and is not
an lvalue. If the array object has register storage class,
the behavior is undefined.
(This is from the published C11 standard. The N1570 draft has an
incorrect reference to the _Alignof operator.)
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-06-16 12:31 -0700 |
| Message-ID | <lntw3fafv5.fsf@kst-u.example.com> |
| In reply to | #112354 |
scott@slp53.sl.home (Scott Lurndal) writes:
> Lew Pitcher <lew.pitcher@digitalfreehold.ca> writes:
[...]
>>Line 7 looks to be...
>> char data[10][4] = "";
>>which describes an array of 10 elements, each containing an array of 4
>>characters. However, your initialization only consists of a single array of
>>1 character (a \0).
>>
>>Apparently, tcc is expecting an initialization that looks something like
>> char data[10][4] = {"","","","","","","","","",""};
It also accepts
char data[10][4] = {""};
or
char data[10][4] = { 0 };
The latter is a valid initializer for an object of any type.
> As "" is of type 'char *', the values assigned to the array will likely
> not be as expected (i.e. either the high-byte (big-endian systems) or the
> low-byte (little-endian systems) of the address to the empty string will
> be stored in data, rather than the expected single NUL byte at column 0
> of each of the ten rows).
No, "" is of type char[1]. Like any array expression, it's implicitly
converted to a pointer expression in most contexts, but a string literal
in an initializer used to initialize an array object is one of the three
explicitly listed exceptions. Using a string literal to initialize an
array of char is perfectly valid, and tcc handles it correctly as far as
I can tell.
> Technically, the correct initializer looks more like:
>
> char data[10][4] = {
> { 0, 1, 2, 3 }, /* row 0 */
> { 4, 5, 6, 7 }, /* row 1 */
> ...
> { 36, 37, 38, 39 }, /* row 9 */
> };
>
> or
> char data[10][4] = {
> { 'a', 'b', 'c', 'd', }, /* row 0 */
> ...
> { 'A', 'B', 'C', 'D', }, /* row 9 */
> };
That's completely unnecessary.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | "James R. Kuyper" <jameskuyper@verizon.net> |
|---|---|
| Date | 2017-06-16 15:45 -0400 |
| Message-ID | <ab8af530-b7f3-9370-d59f-f38b37937276@verizon.net> |
| In reply to | #112354 |
On 06/16/2017 02:01 PM, Scott Lurndal wrote:
> Lew Pitcher <lew.pitcher@digitalfreehold.ca> writes:
...
>> Line 7 looks to be...
>> char data[10][4] = "";
>> which describes an array of 10 elements, each containing an array of 4
>> characters. However, your initialization only consists of a single array of
>> 1 character (a \0).
>>
>> Apparently, tcc is expecting an initialization that looks something like
>> char data[10][4] = {"","","","","","","","","",""};
>
> As "" is of type 'char *', the values assigned to the array will likely
No, the type of "" is char[1], just as the type of "Hello" is char[6].
In many contexts, lvalues of array type get automatically converted into
pointers to the first element of the array, and that might be what
you're thinking of. However, this is NOT one of those cases. One of the
three exceptions is "a string literal used to initialize an array"
(6.3.2.1p3)
Another exception is as the argument of sizeof. You should find that
sizeof "" == sizeof(char*) only if sizeof(char*) == 1.
"An array of character type may be initialized by a character string
literal or UTF−8 string literal, optionally enclosed in braces.
Successive bytes of the string literal (including the terminating null
character if there is room or if the array is of unknown size)
initialize the elements of the array." (6.7.9p14)
[toc] | [prev] | [next] | [standalone]
| From | "James R. Kuyper" <jameskuyper@verizon.net> |
|---|---|
| Date | 2017-06-16 14:07 -0400 |
| Message-ID | <db1eec84-38e3-8501-67af-61fb1cef48e8@verizon.net> |
| In reply to | #112351 |
On 06/16/2017 01:33 PM, DFS wrote:
> How do you use sscanf to split a line into pieces and add each piece to
> an array?
>
>
> #include <stdio.h>
> #include <string.h>
> int main (int argc, char *argv[])
> {
> char line[50] = "";
> char lbls[4] = "";
> char data[10][4] = "";
You're trying to initialize an array of arrays using only a single
array. A better way to initialize it is:
= {0};
That will cause the first element of the first element of data to be
explicitly zero-initialized, and then the rest of the array will be
implicitly zero-initialized. This initializer works for EVERY C type -
the '{' are unneeded for single characters, and might seem insufficient
for arrays with two or more dimensions, but the rules of C are written
to make that work.
> strcpy(line,"Name,Quarter,Product,Sales");
> sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
First problem: The first "%s" will match the entire contents of "line";
"%s" is stopped by white space characters (" \t\v\n\f"), but not by
commas. You have two choices:
The simpler approach is to use a space character to delimit your labels.
The more complicated approach it to use a scanset specifier: "%[^,]".
The '[' and ']' delimit the scan set. The set identifies which
characters will be accepted. The '^' tells it to accept everything other
than the following character, which is ','. Therefore, the scanset
specifier causes it to read in everything up to, but not including, the
following comma.
I recommend the simpler approach.
Second problem (I'll assume you've fixed the first problem):
lbls[0] is a value of type char, which gets promoted to an integer in
this context. "%s" expects to receive a pointer to char.
The most obvious fix to this problem is wrong: &lbls[0] is a pointer to
char (and can be written more simply as lbls+0, with the other arguments
being written as lbls+1, lbls+2, and lbls+3). However, each of those
pointers points at consecutive character in a single array that is only
four characters long. Reading "Name" into lbls+0 will try to fill in
lbls[0] with 'N', lbls[1] with 'a', lbls[2] with 'm', and lbls[3] with
'e', and lbls[4] with '\0'. Problem: there is no lbls[4] - the behavior
of your program is undefined.
Then, attempting to read "Quarter" into lbls+1. That will place 'Q' into
lbls[1], 'u' into lbls[2], 'a' into lbls[3], and attempt to place 'r'
into lbls[4]. As already noted, lbls[4] doesn't exist.
What you want is to set aside enough space to store each of your labels.
If every label has a length that is less than or equal to
MAX_LABEL_LENGTH, then declaring
char lbls[4][MAX_LABEL_LENGTH+1];
would make the above code work (the +1 allows for the terminating null
character). However, there's no easy way to write code with this general
structure that will protect you against the possibility that a given
label is longer than the space you provided.
> printf("%s\n",lbls[2]);
>
> strcpy(line,"Jim,Q1,computer,100");
> sscanf(line, "%s,%s,%s,%s", data[0][0],data[0][1],data[0][2],data[0][3]);
Similarly, if every item has a length that is less than or equal to
MAX_ITEM_LENGTH, then the following declaration would make the code
above work as intended:
char data[10][4][MAX_ITEM_LENGTH+1];
[toc] | [prev] | [next] | [standalone]
| From | "James R. Kuyper" <jameskuyper@verizon.net> |
|---|---|
| Date | 2017-06-16 14:22 -0400 |
| Message-ID | <7c12a92a-5cf7-cb57-2937-a689345687bc@verizon.net> |
| In reply to | #112351 |
On 06/16/2017 01:33 PM, DFS wrote:
> How do you use sscanf to split a line into pieces and add each piece to
> an array?
>
>
> #include <stdio.h>
> #include <string.h>
> int main (int argc, char *argv[])
> {
> char line[50] = "";
> char lbls[4] = "";
> char data[10][4] = "";
>
> strcpy(line,"Name,Quarter,Product,Sales");
> sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
> printf("%s\n",lbls[2]);
>
> strcpy(line,"Jim,Q1,computer,100");
> sscanf(line, "%s,%s,%s,%s", data[0][0],data[0][1],data[0][2],data[0][3]);
>
> return 0;
> }
>
>
> tcc throws an error that doesn't make sense:
> Line 7: error: ',' expected (got ";")
This is an alternate response, taking the above code at face value. I
suspect that in your actual code, the labels and the data might not be
known at compile time, or might change during the running of your
program. However, if they are constant and known at compile time, this
is a much simpler approach:
const char *lbls[4] = {"Name", "Quarter", "Product", "Sales"};
const char *data[10][4] = {
{"Jim", "Q1", "computer", "100"},
// another 9 lines of data.
}
Note that, in particular, using a array of pointers will generally us up
less memory than the approach using a higher-dimensional array with a
fixed length equal to the maximum length of each string - unless the
average string length is only slightly less than the maximum string length.
[toc] | [prev] | [next] | [standalone]
| From | DFS <nospam@dfs.com> |
|---|---|
| Date | 2017-06-16 15:36 -0400 |
| Message-ID | <oi1bko$79b$5@dont-email.me> |
| In reply to | #112359 |
On 6/16/2017 2:22 PM, James R. Kuyper wrote: > This is an alternate response, taking the above code at face value. I > suspect that in your actual code, the labels and the data might not be > known at compile time, or might change during the running of your > program. Yep. It's input from a .csv file (1st row is labels). I'm now thinking of 2 approaches, one with sscanf and one without: * with sscanf: replace the commas with spaces and use sscanf the way you indicated. sscanf(line,"%s %s %s %s",...) Dealing with quote-delimited data will be fun. DFS,Q1,shirts,100 "J Kuyper",Q1,shoes,200 * without sscanf: 'brute-force' read each character and parse data between commas. Thanks for your help.
[toc] | [prev] | [next] | [standalone]
| From | "James R. Kuyper" <jameskuyper@verizon.net> |
|---|---|
| Date | 2017-06-16 17:17 -0400 |
| Message-ID | <a04a8764-0aa3-c22c-58fe-4616c41bd790@verizon.net> |
| In reply to | #112371 |
On 06/16/2017 03:36 PM, DFS wrote:
> On 6/16/2017 2:22 PM, James R. Kuyper wrote:
>
>> This is an alternate response, taking the above code at face value. I
>> suspect that in your actual code, the labels and the data might not be
>> known at compile time, or might change during the running of your
>> program.
>
>
> Yep. It's input from a .csv file (1st row is labels).
>
> I'm now thinking of 2 approaches, one with sscanf and one without:
>
> * with sscanf: replace the commas with spaces and use sscanf the way
> you indicated.
>
> sscanf(line,"%s %s %s %s",...)
>
>
> Dealing with quote-delimited data will be fun.
> DFS,Q1,shirts,100
> "J Kuyper",Q1,shoes,200
>
>
>
> * without sscanf: 'brute-force' read each character and parse data
> between commas.
Note: there's a number of variants of ".csv" running around. If it
becomes important, you might need to locate a precise description of the
form that you're receiving as input. Example of potential complications:
Example:,"This is a quoted string, that contains a comma.",100
Naively using the scanset specifier I mentioned ("%[^,]") would result
in parsing that as having four elements rather than 3.
[toc] | [prev] | [next] | [standalone]
| From | Barry Schwarz <schwarzb@dqel.com> |
|---|---|
| Date | 2017-06-16 12:06 -0700 |
| Message-ID | <era8kc1bnmrfsts4qebb5b60ro2f1n0tlh@4ax.com> |
| In reply to | #112351 |
On Fri, 16 Jun 2017 13:33:06 -0400, DFS <nospam@dfs.com> wrote:
>How do you use sscanf to split a line into pieces and add each piece to
>an array?
>
>
>#include <stdio.h>
>#include <string.h>
>int main (int argc, char *argv[])
>{
> char line[50] = "";
> char lbls[4] = "";
> char data[10][4] = "";
>
> strcpy(line,"Name,Quarter,Product,Sales");
> sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
Obviously this cannot work since lbls[i] will evaluate to the value of
a single char, not to an address which is what %s requires.
> printf("%s\n",lbls[2]);
>
> strcpy(line,"Jim,Q1,computer,100");
> sscanf(line, "%s,%s,%s,%s", data[0][0],data[0][1],data[0][2],data[0][3]);
This would at least have the correct form if you used data[0],
data[1], etc. However it still cannot execute properly since data[2]
is an array of 4 char any you are attempting to store a string of 9
char in that array.
> return 0;
>}
>
>
>tcc throws an error that doesn't make sense:
>Line 7: error: ',' expected (got ";")
>
--
Remove del for email
[toc] | [prev] | [next] | [standalone]
| From | Real Troll <Real.Troll@Trolls.com> |
|---|---|
| Date | 2017-06-16 17:30 -0400 |
| Message-ID | <oi1ie7$658$1@gioia.aioe.org> |
| In reply to | #112351 |
On 16/06/2017 18:33, DFS wrote:
> How do you use sscanf to split a line into pieces and add each piece
> to an array?
>
>
> #include <stdio.h>
> #include <string.h>
> int main (int argc, char *argv[])
> {
> char line[50] = "";
> char lbls[4] = "";
> char data[10][4] = "";
>
> strcpy(line,"Name,Quarter,Product,Sales");
> sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
> printf("%s\n",lbls[2]);
>
> strcpy(line,"Jim,Q1,computer,100");
> sscanf(line, "%s,%s,%s,%s",
> data[0][0],data[0][1],data[0][2],data[0][3]);
>
> return 0;
> }
>
>
> tcc throws an error that doesn't make sense:
> Line 7: error: ',' expected (got ";")
>
>
Would something like this work for you?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main ()
{
char line[50], lbls1[10], lbls2[10], lbls3[10], lbls4[10] ;
strcpy(line,"Name Quarter Product Sales");
sscanf(line, "%s %s %s %s", lbls1, lbls2, lbls3, lbls4);
printf("%s %s %s %s\n",lbls1, lbls2, lbls3, lbls4);
return 0;
}
[toc] | [prev] | [next] | [standalone]
| From | ike@iceland.freeshell.org |
|---|---|
| Date | 2017-06-17 16:49 +0000 |
| Message-ID | <slrn3vfsokanie.hoa.ike@iceland.freeshell.org> |
| In reply to | #112351 |
On 2017-06-16, DFS <nospam@dfs.com> wrote:
> How do you use sscanf to split a line into pieces and add each piece to
> an array?
>
>
> #include <stdio.h>
> #include <string.h>
> int main (int argc, char *argv[])
> {
> char line[50] = "";
> char lbls[4] = "";
> char data[10][4] = "";
>
> strcpy(line,"Name,Quarter,Product,Sales");
> sscanf(line, "%s,%s,%s,%s", lbls[0],lbls[1],lbls[2],lbls[3]);
> printf("%s\n",lbls[2]);
>
> strcpy(line,"Jim,Q1,computer,100");
> sscanf(line, "%s,%s,%s,%s", data[0][0],data[0][1],data[0][2],data[0][3]);
>
> return 0;
> }
>
>
> tcc throws an error that doesn't make sense:
> Line 7: error: ',' expected (got ";")
#include <stdio.h>
int main(void)
{
char line[50] = "Name,Quarter,Product,Sales";
char data[4][10]; /* changed [10][4] to [4][10] */
/* change "%s" to "%[^,]" */
sscanf(line, "%[^,],%[^,],%[^,],%s", data[0], data[1], data[2], data[3]);
puts(data[0]);
puts(data[1]);
puts(data[2]);
puts(data[3]);
return 0;
}
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | comp.lang.c
csiph-web