Head And Tail
November 13, 2015
We read the file by line, writing the first line when we see it, then saving each line as we read the next until reaching the end of the file, when we write the saved line:
(define (head&tail file-name)
(with-input-from-file file-name
(lambda ()
(let ((prev (read-line)))
(display prev) (newline)
(let loop ((line (read-line)) (prev prev))
(if (eof-object? line)
(begin (display prev) (newline))
(loop (read-line) line)))))))
And here’s a sample run:
> (head&tail "gettysburg") Four score and seven years ago, shall not perish from the earth.
We used read-line from the Standard Prelude. You can run the program at http://ideone.com/qlfpFR, where the program has been modified to read standard input.
Scala with iterator
def headAndTail(in: String) = { val lines = Source.fromFile(in).getLines() println(lines.next()) while (lines.hasNext) { val last = lines.next if (!lines.hasNext) println(last) } }Haskell:
FSharp :
Haskell:
headTail filename = (liftM lines $ readFile filename) >>= mapM_ putStrLn . (zipWith ($) [head,last]) . repeathead -1 $1; tail -1 $1
I memory-mapped the file as a byte array. I wanted to learn about that.
A source says it is guaranteed that an ASCII byte cannot occur in the middle of a UTF-8 character; other assumptions about what line-end characters occur where in the file remain because I couldn’t be bothered.
# tiedoston ht.jl ensimmäinen rivi """Displays the first and last line of the named file, assuming way too much about the byte-level contents of the files.""" function peek(name) s = Mmap.mmap(name, Array{UInt8,1}, filesize(name)) print("head: ", utf8(s[1:findfirst(s, 0x0a)])) print("tail: ", utf8(s[findprev(s, 0x0a, end - 1) + 1:end])) end peek(ARGS[1]) # tiedoston ht.jl viimeinen riviTesting with the source code file itself:
// C#:
string[] lines = File.ReadAllLines(@”file.txt”);
if (lines.Length != 0)
{
MessageBox.Show(lines[0] + “\n” + lines[lines.Length – 1]);
}
Here’s a C++ solution that just uses some basic Unix system functions – sbrk and brk for memory allocation, read and write for I/0. Probably should check return codes & allow for partial writes. Could be more intelligent about copying data from the input buffer too.
#include <unistd.h> #include <fcntl.h> static const size_t BSIZE = 256; int main(int argc, char *argv[]) { int fd = (argc == 1) ? 0 : open(argv[1], O_RDONLY); char *buff = (char*)sbrk(2*BSIZE); // Allocate char *start = buff + BSIZE, *end = start + BSIZE; char *p = start; bool first = true; while (true) { ssize_t n = read(fd,buff,BSIZE); if (n <= 0) break; for (ssize_t i = 0; i < n; i++) { if (p > start && *(p-1) == '\n') { if (first) write(1,start,p-start); first = false; p = start; } if (p == end) brk(end += BSIZE); // Extend *p++ = buff[i]; } } write(1,start,p-start); };; A Simple Clisp solution
(defun head-tail(name)
(let (last)
(with-open-file (file name)
(print (read-line file))
(loop for x = (read-line file nil) while x do
(setf last x))
(print last))))
Using the Reverse_file_buf from my solution to the 17 November 2015 exercise…
It’s pretty fast even on large files (despite the inefficiencies of Reverse_file_buf) because it jumps straight to the end to find the last line.
#include <algorithm> #include <cctype> #include <fstream> #include <iostream> #include <string> #include <vector> #include "Reverse_file_buf.hpp" //Ignores lines with only whitespace or control characters. //Not Unicode friendly. std::string find_first_line(std::istream& in) { std::string line; while (std::getline(in, line)) { if (std::any_of(line.begin(), line.end(), ::isgraph)) { return line; break; } } return ""; } int main(int argc, char** argv) { std::vector<std::string> args(argv + 1, argv + argc); if (args.empty()) args.push_back("praxis.cpp"); for (auto arg: args) { std::ifstream forwards(arg); std::cout << find_first_line(forwards) << '\n'; Reverse_file_buf rfb(arg); std::istream backwards(&rfb); auto line = find_first_line(backwards); std::reverse(line.begin(), line.end()); std::cout << line << '\n'; } }Some Racket Scheme solutions .