I am wondering from where newline (4th in example code) is written out from following very simple tcl code. Handling from puts -nonewline is cumbersome. Is there any other tcl command influence this behavior?
set fid [open testout.txt w]
puts $fid 1
puts $fid 2
puts $fid 3
close $fid
Output:
#1:1
#2:2
#3:3
#4:
The puts command always appends a newline to the end of what you ask it to write, unless you pass the -nonewline option. It is a feature of that command, and is most of the time what you tend to want. (The puts command is the only standard Tcl command that writes data to a channel; chan puts is just a different name for the same thing.)
In your case, maybe you don't want the newline at the end of the final line (and should use the option). Or maybe you want to trim the newline from the end before splitting the text into lines when reading it back in. Whether you can tolerate that newline character at the end of the text data in the file depends on what you're doing with it.
Related
I have to delete the last line of file in using tcl script. I know the content so content replacement is also ok. But my content is which has to be replaced by a space or newline character or have to deleted. And my job is in a loop.
Please let me know which is the efficient way, capturing the entire file content each time in loop and replace that string is better or deleting simply the last line.
Please give some script code because I am very new to tcl.
Are we talking about removing the last line from the data on disk or the data in memory? It matters because the approach you use to do those two cases is entirely different.
In memory
Exactly how you manipulate things in memory depends on whether you're representing the data as list of lines or a big string. Both approaches work. (You could be doing something else too, I suppose, but these two are the common obvious ways.)
If you've got your data as a list of lines in memory, you can simply do (assuming you're holding the lines in a variable called theLines):
set theLines [lreplace $theLines end end]
For a particularly large list, there are a few tricks to make it more efficient, but they come down to careful management of references:
# Needs a new enough Tcl (8.5 or 8.6 IIRC)
set theLines [lreplace $theLines[set theLines ""] end end]
Try the first version instead of this if you don't know you need it. Also be aware that if you're wanting to keep the original list of lines around, you should definitely use the first approach.
You might instead have the data in memory as a single big string. In that case, we can use some of Tcl's string searching capabilities to do the job.
set index [string last "\n" $theString end-1]
set theString [string range $theString 0 $index]
The optimisation mentioned above in relation to lreplace is also applicable here (with all the same caveats):
set index [string last "\n" $theString end-1]
set theString [string range $theString[set theString ""] 0 $index]
On disk
When working on disk, things are different. There you need to be much more careful since you can't undo changes easily. There are two general approaches:
Read the file into memory, do the change there (using the techniques above), and do a (destructive) ordinary write out. This is the approach you need when you are doing many other changes anyway (e.g., removing a line from the middle, adding a line to the middle, adding or removing characters from a line in the middle).
set filename "..."
# Open a file and read its lines into a list
set f [open $filename]
set theLines [split [read $f] "\n"]
close $f
# Transform (you should recognise this from above)
set theLines [lreplace $theLines end end]
# Write the file back out
set f [open $filename "w"]
puts -nonewline $f [join $theLines "\n"]
close $f
Find where the data you don't want starts as an offset in the file and truncate the file at that point. This is the right approach with a very large file, but it is rather more sophisticated.
set f [open $filename "r+"]; # NEED the read-write mode!
seek $f -1000 end; # Move to a little bit before the end of the file.
# Unnecessary, and guesswork, but can work and will
# speed things up for a big file very much
# Find the length that we want the file to become. We do this by building a list of
# offsets into the file.
set ptrList {}
while {![eof $f]} {
lappend ptrList [tell $f]
gets $f
}
# The length we want is one step back from the end of the list
set wantedLength [lindex $ptrList end-1]
# Do the truncation!
chan truncate $f $wantedLength
close $f
However you do the disk transformations, make sure you test on a trash file before applying it to anything real! In particular, I've not checked what the truncation method does on a file without a newline at the end. It probably works, but you should test.
I have a very large text file from which I have to extract some data. I read the file line by line and look for keywords. As I know that the keywords I am looking for are much closer to the end of the file than to the beginning, I wonder if it is possible to read the file starting at the last row instead of the first. I then would use an aditional keyword which indicates "everything beyound this word is not of interesst" and stop reading.
Is that possible ?
I don't know how performant this would be, but run the file through tac and read from that:
set fh [open "|tac filename"]
# read from last line to first
while {[gets $fh line] != -1} {...
Another tactic would be to read the last, say, 5000 bytes of the file (using seek), split on newlines and examine those lines, then seek to position 10000 from the end and read the "next" 5000 bytes, etc.
No it is not possible (in any runtime/language I'm aware of, Tcl included).
So decide on a buffer side and read your file by seeking backwards and trying to read a full buffer each time.
Note that you have to observe certain possibilities:
The file might be smaller than the size of your buffer.
It seems you're dealing with a text file, and you want to process it line-wise. If so, observe that if the code is cross-platform or has to work on Windows you have to deal with the case when the data placed in the buffer by the last read operation starts with LF, and the next read operation—of the preceding chunk—will end with CR—that is, your EOL marker will be split across the buffers.
You might want to take a look at the implementation of Tcl_GetsObj() in the generic/tclIO.c file in the Tcl source code—it deals with split CRLFs on normal ("forward") reading of a textual string from a file.
The simplest way to grab the end of a file for searching, assuming you don't know the size of the records (i.e., the line lengths) is to grab too much and work with that.
set f [open $filename]
# Pick some large value; the more you read, the slower
seek $f -100000 end
# Read to the end, split into lines and *DISCARD FIRST*
set lines [lrange [split [read $f] "\n"] 1 end]
Now you can search with lsearch. (Note that you won't know exactly where in the file your matched line is; if you need that, you have to do quite a lot more work.)
if {[lsearch -glob $lines "*FooBar*"] >= 0} {
...
}
The discarding of the first line from the read section is because you're probably starting reading half way through a line; dropping the first “line” will mean that you've only got genuine lines to deal with. (100kB isn't very much for any modern computer system to search through, but you may be able to constrain it further. It depends on the details of the data.)
package require struct::list
set fp [open "filename.txt"]
set lines [split [read -nonewline $fp] "\n"]
foreach line [struct::list reverse $lines] {
...
}
do something with "$line".
to reverse file , I read the file into a variable "list" line by line pre-pending $list with the current line. That way List is in reverse order of file ..
while {[gets $in line] > -1} {
if [regexp "#" $line] {
continue
}
# reverse the order in variable "list"
set list "$line $list"
}
foreach line $list {
puts "line:$ln line= $line"
""*** process each line as you need ***""
}
I tried to create an AppleScript that reads a text file and puts the contents into a list. The file is a simple text file where every line looks like this: example-"example"
The first is a filename and the other is a folder name.
Here is my code now:
set listOfShows to {}
set theFile to readFile("/Users/anders/Desktop/test.txt")
set Shows to read theFile using delimiter return
repeat with nextLine in Shows
if length of nextLine is greater than 0 then
copy nextLine to the end of listOfShows
end if
end repeat
choose from list listOfShows
on readFile(unixPath)
set foo to (open for access (POSIX file unixPath))
set txt to (read foo for (get eof foo))
close access foo
return txt
end readFile
When I run that the output I get this:
error "Can not change \"Game.of.Thrones-\\\"Game Of \" to type file." number -1700 from "Game.of.Thrones-\"Game Of " to file"
My list looks like this: Game.of.Thrones-"Game Of Thrones" and two more lines like that.
The error is that you are trying to read the contents of a file (the first file you read) as a file. Getting the paragraphs of text will break it apart at return/linefeed boundaries, which usually works better than trying to guess what end of line character(s) were used in the file.
You also don't need the whole open for access thing when just reading files, so your script can be reduced to just
set listOfShows to {}
set Shows to paragraphs of (read POSIX file "/Users/anders/Desktop/test.txt")
repeat with nextLine in Shows
if length of nextLine is greater than 0 then
copy nextLine to the end of listOfShows
end if
end repeat
choose from list listOfShows
read uses MacRoman by default, so it jumbles up non-ASCII characters in UTF-8 files unless you add as «class utf8». (as Unicode text is UTF-16.)
paragraphs of (read POSIX file "/tmp/test.txt") as «class utf8»
paragraphs of also works with CRLF and CR line endings. This doesn't, but it ignores the last line if it's empty:
read POSIX file "/tmp/test.txt" as «class utf8» using delimiter linefeed
set milefile to ((path to desktop as text) & "Alert.txt")
set theFileContents to (read file milefile)
display dialog theFileContents
AppleScript’s Language Reference states on page 120:
A series of characters beginning immediately after either the first character after the end of the preceding paragraph or the beginning of the text and ending with either a carriage return character (\r), a linefeed character (\n), a return/linefeed pair (\r\n), or the end of the text. The Unicode "paragraph separator" character (U+2029) is not supported.
So, U+8232 is ignored and AppleScript returns the whole text from the file…
U+8232 is used in TextEdit as the CR character…
Is there a combination of VT100 escape sequences that will allow my C program to print something like:
Waiting......
to a console, in such a way that the dots appear one by one? Essentially, I want a command that will let me insert an extra '.' in front of a newline that has already been sent.
I'm looking for a quick one-liner for linux; it does not have to be portable. ncurses is overkill for this.
you can add ESC[K (clear to end of line) to ESC[A (up one line), and print your new line text
an example using Python:
import random, time
for _ in range(100):
print('\x1b[A\x1b[Kthis will print each line cleanly: %d' %(random.randint(0, 100000)))
time.sleep(0.1)
if you really want to be neat about things, use ESC7 (save cursor) and ESC8 (restore cursor)
then, you write your line and at the end of it you use ESC7. at the beginning of the print statement, you use ESC8. note, unless you turn off automatic newlines, this will not work at the bottom of your tty. it will work on all lines but the bottom.
import random, time
print('this will print each dot cleanly: \x1b7')
for _ in range(10):
print('\x1b8.\x1b7')
print('print more foo: %d' %_)
time.sleep(0.1)
for shell scripting (bash), you would use printf "..." without a \n, or echo -n
An easy way to do this is to use the escape sequence
"\x1b[A"
to move the cursor up one line. Then, re-print the "Waiting..." message, with one more dot than the last time.
I tried all options to create a new line in my output file, but still I get a txt-file with everything behind the previous information. Even with this supersimple code:
globals [file]
to setup clear-all
set file "results\\GA1.txt" if is-string? file
[while [file-exists? file]
[set file replace-item (length file - 5) file "11" ]
file-open file] end
to go tick write-to-file end
to write-to-file file-print (word ticks) FILE-TYPE "\n" file-write 1 file-print (word " " 2 ";") file-write 1 file-print (word " " 2 ";") file-print "" ;; blank line end
I do not get blank lines or line breaks. I work in NetLogo 4.1. Does anybody know what could be the problem?
answered at http://netlogo-users.18673.x6.nabble.com/Adding-a-new-line-when-outputting-data-tp4870905p4870909.html where I wrote:
Are you on Windows and using Notepad to view your files? Nearly every
other Windows program these days understands Unix-style line breaks,
but Notepad doesn't.
You forgot to close the file with file-close or to force it to write data to disk with a file-flush.
When you do a file-write the data does not get written to disk immediately. It gets placed in a buffer. When the buffer is large enough the data is written to disk. You can force netlogo to write data to a file by using the file-flush or file-close commands.
The reference suggests using a better editor. But there is a way to make Notepad work.
If you add the string "\r\n" to your data (with file-type or whatever), it works with Notepad.
Notepad uses the older carriage return ("\r")/ new line ("\n") format. The Unix systems just use new line.
Try this, this works for me
file-open "locations.txt"
ask turtles
[file-print xcor]
file-close
if you want more breaks, then use this
file-open "locations.txt"
ask turtles
[file-print xcor file-print "\n"]
file-close