Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Retrieving values from external text file

18 REPLIES 18
Reply
Message 1 of 19
bdsmls
633 Views, 18 Replies

Retrieving values from external text file

I'd like a routine that reads a text file with specific numbers in it. I want the user to be prompted to enter a number and have the routine read the text file. When that number is found return a specified value from the same text file. I'd like to arrange the text file as follows:

 

123456    John Doe

987654    Sally Jones

etc.

 

When the user enters "123456", "John Doe" is returned.

 

Thanks,

Larry

18 REPLIES 18
Message 2 of 19
Kent1Cooper
in reply to: bdsmls


@bdsmls wrote:

I'd like a routine that reads a text file with specific numbers in it. I want the user to be prompted to enter a number and have the routine read the text file. When that number is found return a specified value from the same text file. I'd like to arrange the text file as follows:

 

123456    John Doe

987654    Sally Jones

etc.

 

When the user enters "123456", "John Doe" is returned.

....


If the number part always begins the line [it could have space(s) first], and the name part never begins with a numerical character, in pretty simple terms, perhaps something like this will do [partially tested]:

 

(vl-load-com); just in case

(setq

  fil (open "YourFilePathNameAndExtension" "r")

  num (getint "\nNumber to search for and return name: ")

); end setq

(while (not done)

  (setq lin (read-line fil)

  (if (= (atoi lin) num)

    (setq

      name (vl-string-left-trim " 0123456789" lin)

      done T

    ); end setq

  ); end if

); end while

(prompt name); or do whatever else with it...

(close fil)

 

Make sure to localize at least the 'done' variable in the enclosing routine, or if you run it more than once it won't work after the first time.

Kent Cooper, AIA
Message 3 of 19
Anonymous
in reply to: bdsmls

Would you be ok with seperating each value with a common delimeter?

example:

123456,John Doe

987654,Sally Jones

 

or is the file already made?

 

Assuming this will be some sort of  data file, If you use a delimeter it makes things a whole lot easier and you can have lots more information included in the file.

Message 4 of 19
dgorsman
in reply to: Anonymous

Commas are better separators than spaces, as users tend to throw in hard-tabs when spaces are called for.

----------------------------------
If you are going to fly by the seat of your pants, expect friction burns.
"I don't know" is the beginning of knowledge, not the end.


Message 5 of 19
Anonymous
in reply to: dgorsman

 


@dgorsman wrote:

Commas are better separators than spaces, as users tend to throw in hard-tabs when spaces are called for


I'm confused

 

Did you read my post or did you just want to post something?

Message 6 of 19
bdsmls
in reply to: Anonymous

The file is not yet made so using a comma delimeter is fine

Message 7 of 19
Kent1Cooper
in reply to: bdsmls


@bdsmls wrote:

The file is not yet made so using a comma delimeter is fine


In that case, you could use the same thing I posted earlier, but replace " 0123456789" [with the initial space] with ",0123456789" [with the initial comma], to remove all numerical characters and commas from the left end, and return the remainder from the first character that isn't one of those to the end.

Kent Cooper, AIA
Message 8 of 19
bdsmls
in reply to: Kent1Cooper

Kent, your routine is asking for an integer. All of the numbers the users will enter are above the 32767 limit for an integer. Also you were missing a close ) for the first setq. But i tried your routine using only a four digit integer and "lin" and "name" returned nil

Message 9 of 19
Anonymous
in reply to: bdsmls

I would like to give you a part of code i use, It is simple,powerful and universal, it uses wcmatch so that in your case it can search by number, name, area code, or entire file depending on your the "matchthis" string, it places all found items in a list that you can then do whatever you like with.

 

 

(defun file-to-list (filename matchthis / ftl_list readline )
(if (findfile filename)
(progn
(setq filename (open filename  "r")
       readline (read-line filename)
)
(while  readline
(if (wcmatch readline matchthis)
(setq ftl_list (cons readline ftl_list))
);i
(setq readline (read-line filename))
);w
(close filename)
(reverse ftl_list)
);p
nil
);i
)


For now you can use this with the above function, which will work for what you want currently.

(defun c:getnamebynumber ( / number info)
(setq number (getstring "\nPlease enter phone number: "))
(if (setq info (car (file-to-list "c:/datafile.txt" (strcat  number "*"))))
(substr info (+  (vl-string-search "," info) 2))
)
)



I also have a program to split up delimeted strings if you wanted to get more advanced and  add information to your data file  example: "12345,John Doe,234 E cherry street"

Message 10 of 19
Kent1Cooper
in reply to: bdsmls


@bdsmls wrote:

.... All of the numbers the users will enter are above the 32767 limit for an integer. Also you were missing a close ) for the first setq. But i tried your routine using only a four digit integer and "lin" and "name" returned nil


[Actually the second (setq) is what's missing the closing ) -- I looked at the first one and thought "what are they talking about?"]

 

Funny about the integer limit, because (atoi) doesn't have any trouble returning considerably larger numbers than that, and even working with them in calculations.  So you could just replace (getint) with (getstring), and replace

(if (= (atoi lin) num)

with

(if (= (atoi lin) (atoi num))

Of course, that all depends on the way (atoi) converts numerical characters at the beginning of a string, and ignores anything following those [spaces, letters, etc.].

 

The 'lin' variable will always return nil at the end, because that's what (read-line) returns when the end of the file is reached, and that's what stops the (while) loop.

 

If the 'name' variable returns nil, it should be because it didn't find a line in the file beginning with the specified number.  I don't think it would happen if the file name was wrong -- there should be an error message for that.  If you localized the 'name' variable, it would return nil if you check it afterwards -- that's partly why I put the prompt-or-whatever in there.

 

It worked for me, adding that one ) and using four-digit-number beginnings of lines.  This version works with larger number parts [I tested it with 6-digit ones], and staying with your original space(s) separators rather than some other delimiter:

 

(defun C:test (/ fil num done)
  (vl-load-com)
  (setq
    fil (open "Drive:/File/Path/Filename.txt" "r")
    num (getstring "\nNumber to search for and return name: ")
  ); end setq
  (while (not done)
    (setq lin (read-line fil))
    (if (= (atoi lin) (atoi num))
      (setq
        name (vl-string-left-trim " 0123456789" lin)
        done T
      ); end setq
    ); end if
  ); end while
  (prompt name); or do whatever else with it...
  (close fil)
  (princ)
)

 

Kent Cooper, AIA
Message 11 of 19
bdsmls
in reply to: bdsmls

I've gotten the routine to work fine on it's own. What the routine is for is to prompt the user for their ID number and return their first name and their last name. The problem i'm having now is getting it to work within another routine. The other routine it's intended for is a routine that inserts a title block. The user is prompted for their ID number and that number along with their first and last name would be added to the title block via attributes. I have limited knowledge of lisp so i'm sure i've missed something simple.

This is my version of the routine which as i stated works fine on its own:

 

(DEFUN C:ID()
(SETQ FILE1(STRCAT ICADPATH "ID.FMT"))
(SETQ ID#(GETSTRING "\nID number: "))
(SETQ FIL(OPEN FILE1 "r"))
(WHILE(NOT DONE)
(PROGN
(SETQ LIN(READ-LINE FIL))
(IF(= (ATOI LIN) (ATOI ID#))
(PROGN
(SETQ NAME(TRYM(SUBSTR LIN 21 100)))
(SETQ FIRST(TRYM(SUBSTR NAME 1 15)))
(SETQ LAST(TRYM(SUBSTR NAME 21)))
))))
(CLOSE FIL)
(PRINC))

 

This is part of the code in the title block routine. The routine dies after you enter the ID number. The value for name though is returned correctly but first and last are returned as nil:

 

(SETQ ID#(GETSTRING "\nID number (. for none): "))

(IF(= ID# ".")(SETQ ID# "")

(PROGN

(SETQ FILE1(STRCAT ICADPATH "ID.FMT"))
(SETQ FIL(OPEN FILE1 "r"))
(WHILE(NOT DONE)
(SETQ LIN(READ-LINE FIL))
(IF(= (ATOI LIN) (ATOI ID#))
(PROGN
(SETQ NAME(TRYM(SUBSTR LIN 21 100)))
(SETQ FIRST(TRYM(SUBSTR NAME 1 15)))
(SETQ LAST(TRYM(SUBSTR NAME 21)))
)))
(CLOSE FIL)

))

Message 12 of 19
Anonymous
in reply to: bdsmls

(SETQ ID#(GETSTRING "\nID number (. for none): "))
(IF(= ID# ".")
(SETQ ID# "")
(PROGN
(SETQ FILE1(STRCAT ICADPATH "ID.FMT"))
(SETQ FIL(OPEN FILE1 "r"))
(WHILE(NOT DONE) <---- done is not set during while or when string is found, so it may be looping continually.
(SETQ LIN(READ-LINE FIL))
(IF (= (ATOI LIN) (ATOI ID#))
(PROGN
(SETQ NAME(TRYM(SUBSTR LIN 21 100))) <--- program runs to here for me. (don't have trym function)
(SETQ FIRST(TRYM(SUBSTR NAME 1 15)))
(SETQ LAST(TRYM(SUBSTR NAME 21)))

(setq done T) <--- add this here  and it should work
)))
(CLOSE FIL)
))

 

 

Message 13 of 19
Kent1Cooper
in reply to: bdsmls


@bdsmls wrote:

I've gotten the routine to work fine on it's own. ....

This is my version of the routine which as i stated works fine on its own:

 

(DEFUN C:ID()
(SETQ FILE1(STRCAT ICADPATH "ID.FMT"))
(SETQ ID#(GETSTRING "\nID number: "))
(SETQ FIL(OPEN FILE1 "r"))
(WHILE(NOT DONE)
(PROGN
(SETQ LIN(READ-LINE FIL))
(IF(= (ATOI LIN) (ATOI ID#))
(PROGN
(SETQ NAME(TRYM(SUBSTR LIN 21 100)))
(SETQ FIRST(TRYM(SUBSTR NAME 1 15)))
(SETQ LAST(TRYM(SUBSTR NAME 21)))
))))
(CLOSE FIL)
(PRINC))

...


You don't include the (TRYM) function, so I couldn't say whether that has anything to do with it.  I don't see anything obvious about the included-in-a-larger-routine part that would explain the problem, but I notice a couple of things about the stand-alone version:

 

The whole purpose of my 'done' variable was so that once it finds the right line in the file, it can stop looking.  You've eliminated that part, but still have the (not done) test in there, which serves no purpose.  If you don't mind having it continue to look through every line in the file, even after it's found the right one, you may as well skip that test, and just use the reading of lines in the file as the check, which will stop the while loop after it's reached the end of the file.

 

Neither of your (progn) functions is necessary.  The first one isn't, because you can do any number of things in a (while) loop following the test part, without needing to "wrap" them in a (progn) function.  That's only needed with the 'then' or 'else' arguments in an (if) function -- those require a single function as an argument, so if you need to do more than one thing, you need to combine them into one (progn).  But (while) doesn't have that limitation, nor (foreach) nor (repeat) nor (cond) nor some other things.

 

The second one is unnecessary, because you can combine multiple variable settings into one (setq) function, and that one can be the 'then' argument for (if), and since it's only one function, it doesn't need the (progn) around it.

 

That routine could be done this more condensed way:

 

(DEFUN C:ID()
  (SETQ

    FILE1(STRCAT ICADPATH "ID.FMT")
    ID#(GETSTRING "\nID number: ")
    FIL(OPEN FILE1 "r")

  ); end setq
  (WHILE(SETQ LIN(READ-LINE FIL))
    (IF(= (ATOI LIN) (ATOI ID#))
      (SETQ

        NAME(TRYM(SUBSTR LIN 21 100))
        FIRST(TRYM(SUBSTR NAME 1 15))
        LAST(TRYM(SUBSTR NAME 21))

      ); end setq
    ); end if

  ); end while
  (CLOSE FIL)
  (PRINC)

)

Kent Cooper, AIA
Message 14 of 19
Kent1Cooper
in reply to: Anonymous


@Anonymous wrote:

....

(setq done T) <--- add this here  and it should work
....


If you go back to doing it that way, include 'done' in the localized variables of the larger routine, or it will work only the first time.  [Maybe that's all it needs to, but still....]

Kent Cooper, AIA
Message 15 of 19
Anonymous
in reply to: Kent1Cooper

yes, the code is a little messy, if the OP wants to use his code its ok with me, not they way i would do it but it will force it to quit looping once number is found and save the program from having to read the whole file unnecessarily.

I believe (while (setq line (read-line file)) is the best method, no need to add an extra variable to program

Message 16 of 19
Anonymous
in reply to: Kent1Cooper

 


@Kent1Cooper wrote:

@bdsmls wrote:

I've gotten the routine to work fine on it's own. ....

This is my version of the routine which as i stated works fine on its own:

 

(DEFUN C:ID()
(SETQ FILE1(STRCAT ICADPATH "ID.FMT"))
(SETQ ID#(GETSTRING "\nID number: "))
(SETQ FIL(OPEN FILE1 "r"))
(WHILE(NOT DONE)
(PROGN
(SETQ LIN(READ-LINE FIL))
(IF(= (ATOI LIN) (ATOI ID#))
(PROGN
(SETQ NAME(TRYM(SUBSTR LIN 21 100)))
(SETQ FIRST(TRYM(SUBSTR NAME 1 15)))
(SETQ LAST(TRYM(SUBSTR NAME 21)))
))))
(CLOSE FIL)
(PRINC))

...


You don't include the (TRYM) function, so I couldn't say whether that has anything to do with it.  I don't see anything obvious about the included-in-a-larger-routine part that would explain the problem, but I notice a couple of things about the stand-alone version:

 

The whole purpose of my 'done' variable was so that once it finds the right line in the file, it can stop looking.  You've eliminated that part, but still have the (not done) test in there, which serves no purpose.  If you don't mind having it continue to look through every line in the file, even after it's found the right one, you may as well skip that test, and just use the reading of lines in the file as the check, which will stop the while loop after it's reached the end of the file.

 

Neither of your (progn) functions is necessary.  The first one isn't, because you can do any number of things in a (while) loop following the test part, without needing to "wrap" them in a (progn) function.  That's only needed with the 'then' or 'else' arguments in an (if) function -- those require a single function as an argument, so if you need to do more than one thing, you need to combine them into one (progn).  But (while) doesn't have that limitation, nor (foreach) nor (repeat) nor (cond) nor some other things.

 

The second one is unnecessary, because you can combine multiple variable settings into one (setq) function, and that one can be the 'then' argument for (if), and since it's only one function, it doesn't need the (progn) around it.

 

That routine could be done this more condensed way:

 

(DEFUN C:ID()
  (SETQ

    FILE1(STRCAT ICADPATH "ID.FMT")
    ID#(GETSTRING "\nID number: ")
    FIL(OPEN FILE1 "r")

  ); end setq
  (WHILE(SETQ LIN(READ-LINE FIL))
    (IF(= (ATOI LIN) (ATOI ID#))
      (SETQ

        NAME(TRYM(SUBSTR LIN 21 100))
        FIRST(TRYM(SUBSTR NAME 1 15))
        LAST(TRYM(SUBSTR NAME 21))

      ); end setq
    ); end if

  ); end while
  (CLOSE FIL)
  (PRINC)

)


 

Great kent, Works on my end.

 

I would suggest that you ( bdsmls ) set those variables to nil after using them. no need to keep them global as i call it.

 

 

 

Message 17 of 19
Kent1Cooper
in reply to: Anonymous


@Anonymous wrote:

....

I believe (while (setq line (read-line file)) is the best method, no need to add an extra variable to program


I expect that choice would be affected by how large a file the routine is looking through.  Given the size of the ID numbers they're talking about, it could be a really long one, enough that the extra 'done' variable could well be worth the overhead, if it means the routine doesn't have to look through the entire file every time.  If it's not a long file, a User would never detect the time it would take to go through it all, so it probably wouldn't matter.

Kent Cooper, AIA
Message 18 of 19
bdsmls
in reply to: bdsmls

The problem was that i had forgotten the (setq done t). The routine works fine now.

Thanks guys for helping me out with this.

 

Larry

Message 19 of 19
Anonymous
in reply to: Kent1Cooper

 


@Kent1Cooper wrote:

@Anonymous wrote:

....

I believe (while (setq line (read-line file)) is the best method, no need to add an extra variable to program


I expect that choice would be affected by how large a file the routine is looking through.  Given the size of the ID numbers they're talking about, it could be a really long one, enough that the extra 'done' variable could well be worth the overhead, if it means the routine doesn't have to look through the entire file every time.  If it's not a long file, a User would never detect the time it would take to go through it all, so it probably wouldn't matter.


 

 

Couldn't agree with you more, yet the more I think about it if someone is searching a file for only 1 item or line then once it is found there is no need to remain in the "while" loop, and an immediate exit of the loop would be the best even if its a small file its good for future and all files.

 

Im most of my cases im looking for more than one item and must go through the entire file.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost