www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Reading and writing a text file

reply Hytak <Hytak_member pathlink.com> writes:
Hi
I just try few things before starting a little program in D, and I will need to
read and write things to a file.
I know how to do it in C++ and a little bit in C, but the D's fonctions to read
and write to file seems to be a mix of C and C++.
What I want to to is to open a .txt and to search in it the word "MYWORRD".
Do we have to use getc?
What should it look like?

I saw the functions read, write, append, ungetc and all but it should be nice
someone write a deeper tutorial on them (does it exist?)
I saw on http://www.quit-clan.de/docwiki/view.php?pageid=3 that there was some
example, nice to look... but now really what I am looking for.
Mar 20 2005
parent reply Hytak <Hytak_member pathlink.com> writes:
Well I forgot: what I want to do is to read the word after "MYWORD:" and to
store it into a char[] or a string.



In article <d1k1n8$l0r$1 digitaldaemon.com>, Hytak says...
Hi
I just try few things before starting a little program in D, and I will need to
read and write things to a file.
I know how to do it in C++ and a little bit in C, but the D's fonctions to read
and write to file seems to be a mix of C and C++.
What I want to to is to open a .txt and to search in it the word "MYWORRD".
Do we have to use getc?
What should it look like?

I saw the functions read, write, append, ungetc and all but it should be nice
someone write a deeper tutorial on them (does it exist?)
I saw on http://www.quit-clan.de/docwiki/view.php?pageid=3 that there was some
example, nice to look... but now really what I am looking for.

Mar 20 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 20 Mar 2005 15:05:41 +0000 (UTC), Hytak wrote:

 Well I forgot: what I want to do is to read the word after "MYWORD:" and to
 store it into a char[] or a string.
 
 
 
 In article <d1k1n8$l0r$1 digitaldaemon.com>, Hytak says...
Hi
I just try few things before starting a little program in D, and I will need to
read and write things to a file.
I know how to do it in C++ and a little bit in C, but the D's fonctions to read
and write to file seems to be a mix of C and C++.
What I want to to is to open a .txt and to search in it the word "MYWORRD".
Do we have to use getc?
What should it look like?

I saw the functions read, write, append, ungetc and all but it should be nice
someone write a deeper tutorial on them (does it exist?)
I saw on http://www.quit-clan.de/docwiki/view.php?pageid=3 that there was some
example, nice to look... but now really what I am looking for.


Here is some code that finds a text string in a file. You can use something like this as a starting point. <code> import std.stdio; import std.file; import std.string; int main(char[][] pArg) { char[][] lFileText; int lPos; int lByteNum; struct FileRef { int LineNo; int ByteNo; }; FileRef[] lFound; if (! std.file.exists( pArg[1] )) { writefln("File '%s' not found.", pArg[1]); return 1; } // Read the entire file in and split it into lines. lFileText = std.string.splitlines(cast(char[]) std.file.read(pArg[1])); // Examine each line in turn. There could be multiple hits/line. foreach(int i, char[] lLine; lFileText) { lByteNum = 0; while((lPos = std.string.find(lLine[lByteNum..$], pArg[2])) != -1) { lByteNum += lPos + 1; lFound.length = lFound.length + 1; lFound[$-1].LineNo = i+1; lFound[$-1].ByteNo = lByteNum; } } if (lFound.length == 0) { writefln("File '%s' does not contain '%s'", pArg[1],pArg[2]); return 2; } // Display each occurance. writefln("File '%s' has '%s' at Line:Column ...", pArg[1],pArg[2]); foreach(FileRef lRef; lFound) { writefln("%8d:%d", lRef.LineNo, lRef.ByteNo); } return 0; } </code> -- Derek Parnell Melbourne, Australia 21/03/2005 2:19:17 AM
Mar 20 2005
next sibling parent Hytak <Hytak_member pathlink.com> writes:
Thanks a lot, it will really help me.



In article <zcui0k32ti67$.p1x9mhr2cj8a$.dlg 40tude.net>, Derek Parnell says...
On Sun, 20 Mar 2005 15:05:41 +0000 (UTC), Hytak wrote:

 Well I forgot: what I want to do is to read the word after "MYWORD:" and to
 store it into a char[] or a string.
 
 
 
 In article <d1k1n8$l0r$1 digitaldaemon.com>, Hytak says...
Hi
I just try few things before starting a little program in D, and I will need to
read and write things to a file.
I know how to do it in C++ and a little bit in C, but the D's fonctions to read
and write to file seems to be a mix of C and C++.
What I want to to is to open a .txt and to search in it the word "MYWORRD".
Do we have to use getc?
What should it look like?

I saw the functions read, write, append, ungetc and all but it should be nice
someone write a deeper tutorial on them (does it exist?)
I saw on http://www.quit-clan.de/docwiki/view.php?pageid=3 that there was some
example, nice to look... but now really what I am looking for.


Here is some code that finds a text string in a file. You can use something like this as a starting point. <code> import std.stdio; import std.file; import std.string; int main(char[][] pArg) { char[][] lFileText; int lPos; int lByteNum; struct FileRef { int LineNo; int ByteNo; }; FileRef[] lFound; if (! std.file.exists( pArg[1] )) { writefln("File '%s' not found.", pArg[1]); return 1; } // Read the entire file in and split it into lines. lFileText = std.string.splitlines(cast(char[]) std.file.read(pArg[1])); // Examine each line in turn. There could be multiple hits/line. foreach(int i, char[] lLine; lFileText) { lByteNum = 0; while((lPos = std.string.find(lLine[lByteNum..$], pArg[2])) != -1) { lByteNum += lPos + 1; lFound.length = lFound.length + 1; lFound[$-1].LineNo = i+1; lFound[$-1].ByteNo = lByteNum; } } if (lFound.length == 0) { writefln("File '%s' does not contain '%s'", pArg[1],pArg[2]); return 2; } // Display each occurance. writefln("File '%s' has '%s' at Line:Column ...", pArg[1],pArg[2]); foreach(FileRef lRef; lFound) { writefln("%8d:%d", lRef.LineNo, lRef.ByteNo); } return 0; } </code> -- Derek Parnell Melbourne, Australia 21/03/2005 2:19:17 AM

Mar 20 2005
prev sibling parent reply AEon <AEon_member pathlink.com> writes:
Derek Parnell says...

Very useful and quite nifty code example. My comments are not actually so much
directed at you Derek (since you know all that :)), but for folks that may want
to expand on the code:


    struct FileRef
    {
        int LineNo;
        int ByteNo;
        char[] Line;     // Added 
    };

I like your using a struct to keep all the infos together. I added a "Line" info to let the user compare search results with the text lines found further down.
    if (! std.file.exists( pArg[1] ))
    {
        writefln("File '%s' not found.", pArg[1]);
        return 1;
    }

Minor thing. I noted when pArg[2] is empty (not specified on the command line), the code will exit. A check of this case + warning might be a good idea.
    // Read the entire file in and split it into lines.   
    lFileText = std.string.splitlines(cast(char[]) std.file.read(pArg[1]));

Clever cast() to get the read to work as input for splitlines :) General Question: I have now seen the specification of full "lib paths" to funtions in several code fragments. Since "splitlines" is actually a unique lib in std one can probably do without the "std.string.", but in the case of "read" specifying the lib path is probably a good thing since read() can be found in std.file and in std.stream. Question, should one always specify full lib paths? IMO it makes reading the code a bit harder, though it is instructive to know exactly where the code/function is from. Experimenting with alias (totally new to me): alias std.string.splitlines aesplitlines; //works lFileText = aesplitlines( cast(char[]) std.file.read(args[1]) ); also works. Use of alias will probably make code harder to read, since every programmer would start using his own set of aliases. Is it thus frowned on, style-wise, to use alias in such cases? ..
            lFound[$-1].ByteNo = lByteNum;
            lFound[$-1].Line = lLine;       // Added

        writefln("%8d:%d:%s", lRef.LineNo, lRef.ByteNo, lRef.Line); //Changed

Added the "Line" info, should you like to see it. AEon
Mar 20 2005
next sibling parent AEon <AEon_member pathlink.com> writes:
Derek,

    foreach(int i, char[] lLine; lFileText)
    {
        lByteNum = 0;
        while((lPos = std.string.find(lLine[lByteNum..$], args[2])) != -1)

        {
            lByteNum += lPos + 1;
            lFound.length = lFound.length + 1;
            lFound[$-1].LineNo = i+1;
            lFound[$-1].ByteNo = lByteNum;
            lFound[$-1].Line = lLine;
        }
    }

Forgot to ask about the new notation: AFAICT "lLine[lByteNum..$]" means "lLine[lByteNum..lLine.length]", right? I have looked in the documentation, but have not been able to find anything on $? I thought I read something about $ in the .D forums, but had thought that way still up for discussion, but not actually in D. AEon
Mar 20 2005
prev sibling next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Sun, 20 Mar 2005 18:52:14 +0000 (UTC), AEon <AEon_member pathlink.com>  
wrote:
 General Question:

 I have now seen the specification of full "lib paths" to funtions in  
 several
 code fragments. Since "splitlines" is actually a unique lib in std one  
 can
 probably do without the "std.string.", but in the case of "read"  
 specifying the
 lib path is probably a good thing since read() can be found in std.file  
 and in
 std.stream.

 Question, should one always specify full lib paths?

 IMO it makes reading the code a bit harder, though it is instructive to  
 know
 exactly where the code/function is from.

I think it's a style choice. I agree that it's instructive, but I also agree that it's harder to read. (those are opinions) It's 'required' in the case of "read" because of the ambiguity/collision. I only use it when it's required, or I use an alias as below.
 Experimenting with alias (totally new to me):

    alias std.string.splitlines aesplitlines;		//works
    lFileText = aesplitlines( cast(char[]) std.file.read(args[1]) );

 also works. Use of alias will probably make code harder to read, since  
 every
 programmer would start using his own set of aliases.

 Is it thus frowned on, style-wise, to use alias in such cases?

No. The only style guideline WRT to alias is that "Meaningless Type Aliases" should be avoided, things like: alias void VOID; alias int INT; alias int* pint; http://www.digitalmars.com/d/dstyle.html Personally I would use something like: alias std.file.read read; but I would not use: alias std.file.read bob; //or anything other than 'read' because IMO alias is used to shorten the code and make it easier to follow. Changing the name of a well known function to something else makes it harder to follow the code. Though it may be possible to choose another name that makes more sense in a specific context.. even so the programmer looking at your code has to remember them for the duration so too many of them would definately make that harder. Regan
Mar 20 2005
next sibling parent reply AEon <AEon_member pathlink.com> writes:
Regan Heath:

Personally I would use something like:
   alias std.file.read read;

but I would not use:
   alias std.file.read bob;  //or anything other than 'read'

because IMO alias is used to shorten the code and make it easier to  
follow. Changing the name of a well known function to something else makes  
it harder to follow the code. ...

Ok... does the above alias have some evil side effects (type checking no longer working e.g.), like the #define in C used to do? It's great to be able to ask questions, this way I learn things *a lot* faster. Thanx everyone. AEon
Mar 20 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Sun, 20 Mar 2005 23:05:29 +0000 (UTC), AEon <AEon_member pathlink.com>  
wrote:
 Regan Heath:

 Personally I would use something like:
   alias std.file.read read;

 but I would not use:
   alias std.file.read bob;  //or anything other than 'read'

 because IMO alias is used to shorten the code and make it easier to
 follow. Changing the name of a well known function to something else  
 makes
 it harder to follow the code. ...

Ok... does the above alias have some evil side effects (type checking no longer working e.g.), like the #define in C used to do?

The one above, no, not as far as I can see. Firstly, because it doesn't alias types, and secondly even if it did... http://www.digitalmars.com/d/declaration.html "Type aliases are equivalent to the C typedef." So "alias" is more similar to "typedef" than "define". The biggest reason is that "define" was handled by the pre-processor, effectively doing a find/replace on the source *before* the compiler sees it. http://www.digitalmars.com/d/declaration.html "Aliased types are semantically identical to the types they are aliased to. The debugger cannot distinguish between them, and there is no difference as far as function overloading is concerned. For example: alias int myint; void foo(int x) { . } void foo(myint m) { . }error, multiply defined function foo" So, AIUI "alias" creates a new name for a type, which is treated identically to the old name. "typedef" actually creates a new type. Regards, Regan
Mar 20 2005
prev sibling parent AEon <AEon_member pathlink.com> writes:
Regan Heath:

Personally I would use something like:
   alias std.file.read read;

but I would not use:
   alias std.file.read bob;  //or anything other than 'read'

because IMO alias is used to shorten the code and make it easier to  
follow. Changing the name of a well known function to something else makes  
it harder to follow the code. ...

Ok... does the above alias have some evil side effects (type checking no longer working e.g.), like the #define in C used to do? It's great to be able to ask questions, this way I learn things *a lot* faster. Thanx everyone. AEon
Mar 20 2005
prev sibling parent Derek Parnell <derek psych.ward> writes:
On Sun, 20 Mar 2005 18:52:14 +0000 (UTC), AEon wrote:

 Derek Parnell says...
 
 Very useful and quite nifty code example. My comments are not actually so much
 directed at you Derek (since you know all that :)), but for folks that may want
 to expand on the code:

LOL. I whipped up this code in ten minutes at 2:20AM so I expect it could have been more polished!
 
    struct FileRef
    {
        int LineNo;
        int ByteNo;
        char[] Line;     // Added 
    };

I like your using a struct to keep all the infos together. I added a "Line" info to let the user compare search results with the text lines found further down.

    if (! std.file.exists( pArg[1] ))
    {
        writefln("File '%s' not found.", pArg[1]);
        return 1;
    }

Minor thing. I noted when pArg[2] is empty (not specified on the command line), the code will exit. A check of this case + warning might be a good idea.

I didn't bother with an really serious argument checking. In a real application one would improve that situation.
 
    // Read the entire file in and split it into lines.   
    lFileText = std.string.splitlines(cast(char[]) std.file.read(pArg[1]));

Clever cast() to get the read to work as input for splitlines :) General Question: I have now seen the specification of full "lib paths" to funtions in several code fragments. Since "splitlines" is actually a unique lib in std one can probably do without the "std.string.", but in the case of "read" specifying the lib path is probably a good thing since read() can be found in std.file and in std.stream. Question, should one always specify full lib paths?

I try to. The reason is mainly that if you don't and later on a new module comes into scope with a same-named member, the code suddenly finds a reason to trouble the compiler. (Given that members don't tend to travel to different modules - they settle down very quickly so recoding a package path is not really a bother).
 IMO it makes reading the code a bit harder, though it is instructive to know
 exactly where the code/function is from.

Yes, it does. And yes it is.
 Experimenting with alias (totally new to me):
 
    alias std.string.splitlines aesplitlines;		//works
    lFileText = aesplitlines( cast(char[]) std.file.read(args[1]) );
 
 also works. Use of alias will probably make code harder to read, since every
 programmer would start using his own set of aliases.
 
 Is it thus frowned on, style-wise, to use alias in such cases?

A common use of alias is to help readability, as you note. You could have done this too... alias std.string.splitlines splitlines; lFileText = splitlines( cast(char[]) std.file.read(args[1]) ); -- Derek Parnell Melbourne, Australia 21/03/2005 7:12:34 AM
Mar 20 2005