I am new to C programming, so bear with me..
There is a text file that have lines like:
00001 John Doe
00002 Ronnie Williams
...
...
Each columns have fixed lengths:
number --> 10 chars long
name ---> 15 chars long
lastname ---> 20 chars long
I cannot use something like " sscanf("%s %s %s"),no,name,lastname)
" because it fails if there one has a midlle name..
I'd appreciate if you help me with that..
Thank you..
> Hi,
>
> I am new to C programming, so bear with me..
>
> There is a text file that have lines like:
>
> 00001 John Doe
> 00002 Ronnie Williams
> ...
> ...
>
> Each columns have fixed lengths:
>
> number --> 10 chars long
> name ---> 15 chars long
> lastname ---> 20 chars long
>
> I cannot use something like " sscanf("%s %s %s"),no,name,lastname)
> " because it fails if there one has a midlle name..
If you know that each "field" is always contained completely within the
widths specified above, for example you never have a case like this:
> 00002 Ronnie Williams
02345 Esmereldamamasita Mylastnameisincrediblylongohmy
If that can't happen, then you can just read in the entire line, with
something like fgets(), or some of the alternatives such as fgetline()
or ggets() that some of the regulars have placed out there.
Once you have an intact line (presumably at least 45 chars long, plus a
newline or a \0), then just chop up the text into segments, one 10
chars long, one 15 chars long, and one 20 chars long.
If you want to trim off trailing spaces, you may do so afterward. If
tabs are used, you would have to know if tabs had a fixed tab width
expansion or not in order to deal with them properly.
--
Randy Howard (2reply remove FOOBAR)
"The power of accurate observation is called cynicism by those
who have not got it." - George Bernard Shaw
Don't use scanf. Use, for example, getc() and a counter.
--
Chuck F (cbfalconer at maineline dot net)
<http://cbfalconer.home.att.net>
Try the download section.
--
Posted via a free Usenet account from http://www.teranews.com
I'd go with a struct that matches the table layout, then use fread()
Jim
--
http://www.ursaMinorBeta.co.uk
"The Sierpinski Gasket makes me want to cry."
- Jonathon Coulton, "Mandelbrot Set"
In fact, i have something like this to fetch the parts:
void strMid(char _source[100], int _where, int _width) { // similar
to Mid() in Basic
char _temp[100];
int _counter;
for(_counter=_where;_counter<=_where+_width;_counter++)
{
_temp[_counter- _where] = _source[_counter];
}
_temp[_width] = 0;
strcpy(_source,_temp);
}
I've tried but couldn't write a string returning version of this
function. :(
In other words, char[] is an alloca() array.
Corrected strMid:
char* strMid(char *_source, unsigned _where, unsigned _width) {
char *temp = calloc(_width + 1, 1); /* malloc + memset(temp, 0,
_width + 1) */
strncpy(temp, _source + _where, _width);
return temp;
}
Note: to prevent memory leaks, call the function like this:
void foo(void) {
char bar[50];
char *baz = strMid(bar, 5, 5);
/* do something with baz */
...
free(baz);
baz = strMid(bar, 10, 7);
/* do something with baz */
...
free(baz);
/* etc */
}
You were using C++, and you posted your question to comp.lang.c
indicating that it was a question about C. Why?
I am not coding in C++, just using VC++ and Turbo C++ to compile..
Thanks a lot Andrey..
You said you were getting unhandled exceptions. How were you achieving
that result if you weren't using C++?
Here is my code.. :
-------------------------------
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
struct temp_type {
char Number[10];
char Name[10];
char lastname[15];
char birthday[10];
char regdate[10];
} student;
char* rtrim(char* str)
{
int n = strlen(str) - 1;
while (n>0)
{
if (*(str+n) != ' ')
{
*(str+n+1) = '\0';
break;
}
else
n--;
}
return str;
}
char* strLeft(char *_source, int _width) {
char *tmp;
tmp=_source;
*(tmp+_width) = '\0';
return tmp;
}
char* strMid(char *_source, int _where, int _width) {
char *_temp;
int _counter;
int counter2=0;
for(_counter=_where;_counter<=_where+_width;_counter++)
{
*(_temp+counter2) = *(_source+_counter);
counter2++;
}
*(_temp+_width) = 0;
return _temp;
}
void ClearScreen(void)
{
system("cls");
}
void MainMenu(void)
{
puts("\n\nStudent Query");
puts("------------------------");
puts("1- student Reg.");
puts("2- Query");
puts("3- Quit\n");
printf("Action: ");
}
void studentQuery(void)
{
int result;
char searching[10];
char _line[55];
char *_number;
char *_name;
char *_lastname;
char *_birthday;
char *_regdate;
int _equal;
char _found=0;
FILE *studentFile;
ClearScreen();
printf("Enter your number: ");
result = scanf("%s",searching);
if( (studentFile = fopen( "student.txt", "rw" )) == NULL )
printf( "STUDENT.TXT cannot be opened!\n" );
else
{
while(fgets(_line, 57, studentFile) != NULL)
{
_number = strLeft(_line,10);
rtrim(_number);
rtrim(_name);
_equal = strncmp(_number, searching, 10);
if (_equal == 0) {
printf ("\nFound ...\n School Number = %s\n", searching);
printf ("Name = %s\n", _name);
_found=1;
break;
}
}
fclose(studentFile);
}
if (_found==0) puts("Cannot find the search criteria.\n");
puts("\nPress any key to return to the main menu.");
getch();
}
void studentReg(void){
int result;
FILE *studentFile;
ClearScreen();
puts("\nStudent Registration");
puts("------------------------");
printf("Number : "); result = scanf("%s", student.Number);
printf("Name : "); result = scanf("%s", student.Name);
printf("LastName : "); result = scanf("%s", student.lastname);
printf("Birthday : "); result = scanf("%s", student.birthday);
printf("Reg. Date : "); result = scanf("%s", student.regdate);
if( (studentFile = fopen( "student.txt", "a" )) == NULL )
printf( "student.TXT cannot be opened!\n" );
else
{
fprintf(studentFile,"%-10.10s%-10.10s%-15.15s%-10.10s%-10.10s
\n",student.Number,student.Name,student.lastname,student.birthday,student.regdate);
fclose(studentFile);
}
}
void main(void) {
char _input;
while (_input != 51) {
ClearScreen();
MainMenu();
_input = getch();
switch(_input) {
case 49: studentReg();
break;
case 50: studentQuery();
break;
}
}
}
----------------- STUDENT.TXT ------------------
00001 John DOE 10.16.196808.19.2002
00002 Ronnie WILLIAMS 03.13.197108.20.2000
--------------------------- EOF ----------------------------
This is a non-standard header. You use it only for the (non-standard)
getch() function; you could use the standard getc() or getchar()
instead.
> #include <string.h>
> #include <stdlib.h>
>
> struct temp_type {
> char Number[10];
> char Name[10];
> char lastname[15];
> char birthday[10];
> char regdate[10];
> } student;
>
>
> char* rtrim(char* str)
> {
> int n = strlen(str) - 1;
It would make more sense for n to be a size_t rather than an int.
> while (n>0)
> {
> if (*(str+n) != ' ')
Why not write "str[n]" rather than "*(str+n)"?
> {
> *(str+n+1) = '\0';
Again, this could be written as "str[n+1]".
> break;
> }
> else
> n--;
> }
> return str;
I find your indentation misleading. Consider using a utility like
"indent".
> }
> char* strLeft(char *_source, int _width) {
Using identifiers starting with underscores is unwise. Many of them
are reserved to the implementation. It's generally not worth the
effort to remember which ones are reserved in which contexts; just
avoid them. (Consider using trailing underscores if you want some
kind of special marker.)
> char *tmp;
> tmp=_source;
> *(tmp+_width) = '\0';
> return tmp;
> }
What is the purpose of tmp? It merely holds a copy of the value of
_source; you never modify it.
Here's a suggested re-implementation of strLeft:
char* strLeft(char *_source, int _width) {
_source[_width] = '\0';
return _source;
}
>
> char* strMid(char *_source, int _where, int _width) {
> char *_temp;
> int _counter;
> int counter2=0;
>
> for(_counter=_where;_counter<=_where+_width;_counter++)
> {
> *(_temp+counter2) = *(_source+_counter);
> counter2++;
> }
> *(_temp+_width) = 0;
'\0' is equivalent and clearer (you use it elsewhere).
>
> return _temp;
> }
Again, _temp is just a copy of _source. Again, you avoid the []
operator for no good reason that I can see.
You might consider using memmove() rather than copying each character
in a loop.
>
> void ClearScreen(void)
> {
> system("cls");
> }
This won't work if the system doesn't happen to have a "cls" command.
Why do you want to clear the screen anyway? What if I run your
program and I have information that I care about on my screen?
>
> void MainMenu(void)
> {
>
> puts("\n\nStudent Query");
> puts("------------------------");
> puts("1- student Reg.");
> puts("2- Query");
> puts("3- Quit\n");
> printf("Action: ");
I suggest adding "fflush(stdout);" here to ensure that the ouptut
actually appears.
A separate call for the blank line before "Action: " would make the
code clearer; it took me a moment to realize what was going on with
the "\n" at the end of the puts() argument.
Consider using letters rather than numbers as inputs.
>
> }
>
> void studentQuery(void)
> {
> int result;
> char searching[10];
> char _line[55];
> char *_number;
> char *_name;
> char *_lastname;
> char *_birthday;
> char *_regdate;
> int _equal;
> char _found=0;
>
> FILE *studentFile;
>
> ClearScreen();
> printf("Enter your number: ");
fflush(stdout);
> result = scanf("%s",searching);
This is dangerous. If the user types more than 9 characters, you'll
overflow your buffer, with arbitrarily bad results. Consider using
fgets() instead, and perhaps using sscanf() to parse the input line in
memory. (There are still some gotchas with that approach; see the
FAQ.)
>
> if( (studentFile = fopen( "student.txt", "rw" )) == NULL )
> printf( "STUDENT.TXT cannot be opened!\n" );
> else
> {
>
>
> while(fgets(_line, 57, studentFile) != NULL)
> {
>
>
> _number = strLeft(_line,10);
> rtrim(_number);
> rtrim(_name);
>
> _equal = strncmp(_number, searching, 10);
I'm not sure why you're using strncmp() rather than strcmp().
>
> if (_equal == 0) {
> printf ("\nFound ...\n School Number = %s\n", searching);
> printf ("Name = %s\n", _name);
>
> _found=1;
> break;
> }
> }
>
> fclose(studentFile);
> }
> if (_found==0) puts("Cannot find the search criteria.\n");
It looks like _equal and _found are flags. Use "if (equal)" and
"if (!found)" rather than comparing to 0.
> puts("\nPress any key to return to the main menu.");
> getch();
getch() is non-standard and non-portable.
> }
>
> void studentReg(void){
> int result;
> FILE *studentFile;
>
> ClearScreen();
>
> puts("\nStudent Registration");
> puts("------------------------");
>
> printf("Number : "); result = scanf("%s", student.Number);
> printf("Name : "); result = scanf("%s", student.Name);
> printf("LastName : "); result = scanf("%s", student.lastname);
> printf("Birthday : "); result = scanf("%s", student.birthday);
> printf("Reg. Date : "); result = scanf("%s", student.regdate);
All these scanf("%s", ...) calls are dangerous; see above.
> if( (studentFile = fopen( "student.txt", "a" )) == NULL )
> printf( "student.TXT cannot be opened!\n" );
> else
> {
>
>
> fprintf(studentFile,"%-10.10s%-10.10s%-15.15s%-10.10s%-10.10s
> \n",student.Number,student.Name,student.lastname,student.birthday,student.regdate);
In the code you posted, the string literal was split across two lines.
> fclose(studentFile);
> }
>
>
> }
>
>
> void main(void) {
This is wrong. The correct declaration is "int main(void)".
> char _input;
>
> while (_input != 51) {
51? What the heck is 51?
Oh, it's the ASCII code for '3'. Change this to
while (_input != '3') {
if you want anyone to be able to read it.
> ClearScreen();
Why??
> MainMenu();
>
> _input = getch();
getch() again.
> switch(_input) {
>
> case 49: studentReg();
> break;
> case 50: studentQuery();
> break;
Use '1' and '2', not 49 and 50.
>
> }
>
> }
return 0;
> }
[...]
After commenting out the "#include <conio.h>", I got the following
warnings:
c.c: In function 'studentQuery':
c.c:121: warning: implicit declaration of function 'getch'
c.c:81: warning: unused variable '_regdate'
c.c:80: warning: unused variable '_birthday'
c.c:79: warning: unused variable '_lastname'
c.c: At top level:
c.c:155: warning: return type of 'main' is not 'int'
c.c: In function 'strMid':
c.c:44: warning: '_temp' is used uninitialized in this function
c.c: In function 'studentQuery':
c.c:78: warning: '_name' is used uninitialized in this function
c.c: In function 'main':
c.c:158: warning: '_input' is used uninitialized in this function
The "used uninitialized in this function" warnings are particularly
serious.
--
Keith Thompson (The_Other_Keith) <ks...@mib.org>
[...]
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
You can read the entire line into a buffer and do things like count
the fields (defining a field as non-isspace characters separated by
isspace characters and begining/end of line).
You can also read a character at a time, validating and accumulating
to per-field buffers, with a finite state machine.
--
Feh. Mad as heck.
I didn't see anything in there that could produce an unhandled
exception. How did you reach the conclusion that this was your
problem? How did you deal with it? Note that C doesn't provide any
mechanisms of any kinds for handling exceptions.
<snip>
Thank you very much Keith, I noted every point you made..
> char *tmp;
> tmp=_source;
> *(tmp+_width) = '\0';
> return tmp;
I don't want to mess with the _source.. All i want to do return a
value
without changing the _source variable..
I couldn't find anything on internet because of my poor English..
Yes, that is exactly what i want to do..
But i don't know how to write a function that doesn't change
the parameter's value, it will just return a value..
Like :
int main (void)
{
char *line="123456";
char *student;
student = strLeft(line,5);
puts(student);
puts(line);
}
char* strLeft(char *_source, int _width)
{
_source[_width] = '\0';
return _source;
}
-------------------------------------
In this example, i want "line" to keep its value, just
return the modified string..
Sorry for my English, hope I made my point..
>
> > > > You were using C++, and you posted your question to comp.lang.c
> > > > indicating that it was a question about C. Why?
> >
> > > I am not coding in C++, just using VC++ and Turbo C++ to compile..
> >
> > You said you were getting unhandled exceptions. How were you
> > achieving that result if you weren't using C++?
>
> Here is my code.. :
What was the extension on the source file code was in?
Brian
".c"
>> char *tmp;
>> tmp=_source;
>> *(tmp+_width) = '\0';
>> return tmp;
>I don't want to mess with the _source.. All i want to do return a
>value
>without changing the _source variable..
That code will not do what you want. _source points to some array,
and copying the pointer results in something that still points
to the same place. When you then *(tmp+_width) = '\0'; you are
changing the original storage.
You probably need something like,
size_t idx;
char *tmp = malloc(width+1);
if (!tmp) { /* you ran out of memory, do something */ }
for (idx = 0; idx < width; idx++)
tmp[idx] = _source[idx];
tmp[width] = '\0';
return tmp;
You might be able to shorten this code by using strncpy().
--
"I will speculate that [...] applications [...] could actually see a
performance boost for most users by going dual-core [...] because it
is running the adware and spyware that [...] are otherwise slowing
down the single CPU that user has today" -- Herb Sutter
So VC++ should have been compiling that as C code. I'd assume the same
of Turbo C++, but it's been a long time since I used that.
Brian
You're welcome.
>> char *tmp;
>> tmp=_source;
>> *(tmp+_width) = '\0';
>> return tmp;
>
> I don't want to mess with the _source.. All i want to do return a
> value
> without changing the _source variable..
You don't change the value of _source, but you do change the data that
_source points to. You do this regardless of whether the value of
_source is copied to tmp or not.
If you want to return a pointer to a distinct string, without
modifying the string to which _source points at all, you'll need to
allocate the new string somehow.
And, if strlen(str) returns 0, this is an unsigned quantity, and n
becomes a large positive number (roughly 2 exp 31 is normal). The
following "while (n > 0)" test may take some time to complete.
Good catch!
Using int is still not the best answer, unless you have reason to be
sure that str will never be longer than INT_MAX or so characters.
Using long would be better. Possibly the best solution would be to
re-work the logic so size_t can be used (but I haven't reexamined the
code to confirm this.)
There's not too many options.
- Change the input string.
- Allocate a new string and copy into it, freeing when no longer needed.
- Copy to static array and don't overwrite early.
- Don't use C strings, but rather a (firstcharacteraddress,strlength) pair.
--
SM Ryan http://www.rawbw.com/~wyrmwif/
You face forward, or you face the possibility of shock and damage.