Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #112351 > unrolled thread

Splitting strings into arrays with sscanf

Started byDFS <nospam@dfs.com>
First post2017-06-16 13:33 -0400
Last post2017-06-17 18:25 -0400
Articles 20 on this page of 21 — 9 participants

Back to article view | Back to comp.lang.c


Contents

  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 →


#112351 — Splitting strings into arrays with sscanf

FromDFS <nospam@dfs.com>
Date2017-06-16 13:33 -0400
SubjectSplitting 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]


#112352

Fromscott@slp53.sl.home (Scott Lurndal)
Date2017-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]


#112355

FromDFS <nospam@dfs.com>
Date2017-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]


#112357

Fromscott@slp53.sl.home (Scott Lurndal)
Date2017-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]


#112367

FromKeith Thompson <kst-u@mib.org>
Date2017-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]


#112366

FromKeith Thompson <kst-u@mib.org>
Date2017-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]


#112353

FromLew Pitcher <lew.pitcher@digitalfreehold.ca>
Date2017-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]


#112354

Fromscott@slp53.sl.home (Scott Lurndal)
Date2017-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]


#112361

FromBarry Schwarz <schwarzb@dqel.com>
Date2017-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]


#112370

From"James R. Kuyper" <jameskuyper@verizon.net>
Date2017-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]


#112374

FromKeith Thompson <kst-u@mib.org>
Date2017-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]


#112369

FromKeith Thompson <kst-u@mib.org>
Date2017-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]


#112373

From"James R. Kuyper" <jameskuyper@verizon.net>
Date2017-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]


#112356

From"James R. Kuyper" <jameskuyper@verizon.net>
Date2017-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]


#112359

From"James R. Kuyper" <jameskuyper@verizon.net>
Date2017-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]


#112371

FromDFS <nospam@dfs.com>
Date2017-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]


#112375

From"James R. Kuyper" <jameskuyper@verizon.net>
Date2017-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]


#112363

FromBarry Schwarz <schwarzb@dqel.com>
Date2017-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]


#112376

FromReal Troll <Real.Troll@Trolls.com>
Date2017-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]


#112398

Fromike@iceland.freeshell.org
Date2017-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