Append a record to the end of a text file: Difference between revisions
m (→{{header|REXX}}: removed OVERFLOW from PRE html tags.) |
|||
Line 882: | Line 882: | ||
{{out}} |
{{out}} |
||
<pre>Appended record: xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash</pre> |
<pre>Appended record: xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash</pre> |
||
=={{header|RapidQ}}== |
|||
'''The easy short solution''' |
|||
<lang vb> |
|||
'Short solution: Append record and read last record |
|||
$Include "Rapidq.inc" |
|||
dim file as qfilestream |
|||
dim filename as string |
|||
dim LogRec as string |
|||
'First create our logfile |
|||
filename = "C:\Logfile2.txt" |
|||
file.open(filename, fmCreate) |
|||
file.writeline "jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash" |
|||
file.writeline "jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jsmith:/bin/bash" |
|||
file.close |
|||
'Append record |
|||
file.open(filename, fmOpenWrite) |
|||
file.position = File.size |
|||
file.writeline "xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash" |
|||
file.close |
|||
'Read last record |
|||
file.open (filename, fmOpenRead) |
|||
while not file.EOF |
|||
LogRec = File.Readline |
|||
wend |
|||
showmessage "Appended record: " + LogRec |
|||
</lang> |
|||
{{out}} |
|||
<pre>Appended record: xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash</pre> |
|||
'''Full solution: create an object to handle all functions''' |
|||
<lang vb> |
|||
'Full solution: Create an object with all required fields and |
|||
'build-in functions to append a record and to read the last record |
|||
$include "Rapidq.inc" |
|||
Type TLogFile extends QObject |
|||
Private: |
|||
file as qfilestream |
|||
Public: |
|||
account as string |
|||
password as string |
|||
UID as integer |
|||
GID as integer |
|||
GECOS.fullname as string |
|||
GECOS.office as string |
|||
GECOS.extension as string |
|||
GECOS.homephone as string |
|||
GECOS.email as string |
|||
directory as string |
|||
shell as string |
|||
RSep as string |
|||
GSep as string |
|||
function AppendRecord(LogFile as string) as integer |
|||
with This |
|||
if fileexists(LogFile) then |
|||
Result = .file.open(LogFile, fmOpenWrite) |
|||
else |
|||
Result = .file.open(LogFile, fmCreate) |
|||
end if |
|||
.file.position = .file.size |
|||
.file.writeline .account + .RSep + .password + .RSep + str$(.UID) + .RSep + str$(.GID) + .RSep +_ |
|||
.GECOS.fullname + .GSep + .GECOS.office + .GSep + .GECOS.extension +_ |
|||
.GSep + .GECOS.homephone + .GSep + .GECOS.email + .RSep +_ |
|||
.directory + .RSep + .shell |
|||
.file.close |
|||
end with |
|||
end function |
|||
Function ReadLastRecord(LogFile as string) as string |
|||
dim x as integer |
|||
dim LogRec as string |
|||
dim GECOSRec as string |
|||
With This |
|||
if fileexists(LogFile) then |
|||
.file.open(LogFile, fmOpenRead) |
|||
While not .file.eof |
|||
LogRec = .file.readline |
|||
Wend |
|||
.file.close |
|||
.account = field$(LogRec, .RSep, 1) |
|||
.password = field$(LogRec, .RSep, 2) |
|||
.UID = val(field$(LogRec, .RSep, 3)) |
|||
.GID = val(field$(LogRec, .RSep, 4)) |
|||
GECOSRec = field$(LogRec, .RSep, 5) |
|||
.directory = field$(LogRec, .RSep, 6) |
|||
.shell = field$(LogRec, .RSep, 7) |
|||
.GECOS.fullname = field$(GECOSRec, .GSep, 1) |
|||
.GECOS.office = field$(GECOSRec, .GSep, 2) |
|||
.GECOS.extension = field$(GECOSRec, .GSep, 3) |
|||
.GECOS.homephone = field$(GECOSRec, .GSep, 4) |
|||
.GECOS.email = field$(GECOSRec, .GSep, 5) |
|||
else |
|||
showmessage "Can't read file " + Logfile |
|||
end if |
|||
end with |
|||
End function |
|||
Constructor |
|||
RSep = ":" |
|||
GSep = "," |
|||
End Constructor |
|||
end type |
|||
'--- Now we can use our LogFile object: |
|||
dim LogFile as TLogFile |
|||
'--- Let's save a record: Set field values |
|||
Logfile.account = "jsmith" |
|||
Logfile.password = "X" |
|||
Logfile.UID = 1001 |
|||
Logfile.GID = 1000 |
|||
Logfile.directory = "/home/jsmith" |
|||
Logfile.shell = "/bin/bash" |
|||
Logfile.GECOS.fullname = "Joe Smith" |
|||
Logfile.GECOS.office = "Room 1007" |
|||
Logfile.GECOS.extension = "(234)555-8917" |
|||
Logfile.GECOS.homephone = "(234)555-0077" |
|||
Logfile.GECOS.email = "jsmith@rosettacode.org" |
|||
'--- And save it to our logfile |
|||
Logfile.appendrecord("c:\A test.txt") |
|||
'--- Let's save the second one: Set field values |
|||
Logfile.account = "jdoe" |
|||
Logfile.password = "X" |
|||
Logfile.UID = 1002 |
|||
Logfile.GID = 1000 |
|||
Logfile.directory = "/home/jsmith" |
|||
Logfile.shell = "/bin/bash" |
|||
Logfile.GECOS.fullname = "Jane Doe" |
|||
Logfile.GECOS.office = "Room 1004" |
|||
Logfile.GECOS.extension = "(234)555-8914" |
|||
Logfile.GECOS.homephone = "(234)555-0044" |
|||
Logfile.GECOS.email = "jdoe@rosettacode.org" |
|||
'--- And save it to our logfile |
|||
Logfile.appendrecord("c:\A test.txt") |
|||
'--- And append the last one: Set field values |
|||
Logfile.account = "xyz" |
|||
Logfile.password = "X" |
|||
Logfile.UID = 1003 |
|||
Logfile.GID = 1000 |
|||
Logfile.directory = "/home/xyz" |
|||
Logfile.shell = "/bin/bash" |
|||
Logfile.GECOS.fullname = "X Yz" |
|||
Logfile.GECOS.office = "Room 1003" |
|||
Logfile.GECOS.extension = "(234)555-8913" |
|||
Logfile.GECOS.homephone = "(234)555-0033" |
|||
Logfile.GECOS.email = "xyz@rosettacode.org" |
|||
'--- And save it to our logfile |
|||
Logfile.appendrecord("c:\A test.txt") |
|||
'--- Read last record: load all CSV fields in our LogFile object props |
|||
Logfile.ReadLastRecord("c:\A test.txt") |
|||
'--- And simply Read values from the objects properties |
|||
Print Logfile.account |
|||
Print Logfile.password |
|||
Print Logfile.UID |
|||
Print Logfile.GID |
|||
Print Logfile.directory |
|||
Print Logfile.shell |
|||
Print Logfile.GECOS.fullname |
|||
Print Logfile.GECOS.office |
|||
Print Logfile.GECOS.extension |
|||
Print Logfile.GECOS.homephone |
|||
Print Logfile.GECOS.email |
|||
Print "" |
|||
input "Press enter to exit:";a$ |
|||
</lang> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
Revision as of 01:52, 9 December 2014
You are encouraged to solve this task according to the task description, using any language you may know.
Many systems offer the ability to open a file for writing, such that any data written will be appended to the end of the file. Further, the file operations will always adjust the position pointer to guarantee the end of the file, even in a multitasking environment.
This feature is most useful in the case of log files, where many jobs may be appending to the log file at the same time, or where care must be taken to avoid concurrently overwriting the same record from another job.
Task: Given a two record sample for a mythical "passwd" file:
- Write these records out in the typical system format.
- Ideally these records will have named fields of various types.
- Close the file, then reopen the file for append.
- Append a new record to the file and close the file again.
- Take appropriate care to avoid concurrently overwrites from another job.
- Open the file and demonstrate the new record has indeed written to the end.
account | password | UID | GID | fullname,office,extension,homephone,email | directory | shell |
---|---|---|---|---|---|---|
string | string | int | int | struct(string,string,string,string,string) | string | string |
jsmith | x | 1001 | 1000 | Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org | /home/jsmith | /bin/bash |
jdoe | x | 1002 | 1000 | Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org | /home/jsmith | /bin/bash |
account | password | UID | GID | fullname,office,extension,homephone,email | directory | shell |
---|---|---|---|---|---|---|
string | string | int | int | struct(string,string,string,string,string) | string | string |
xyz | x | 1003 | 1000 | X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org | /home/xyz | /bin/bash |
Resulting file format: should mimic Linux's /etc/passwd file format with particular attention to the "," separator used in the GECOS field. But if the specific language has a particular or unique format of storing records in text file, then this format should be named and demonstrated with an additional example.
Expected output:
Appended record: xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
Finally: Provide a summary of the language's "append record" capabilities in a table. eg.
Data Representation | IO Library |
Append Possible |
Automatic Append |
Multi-tasking Safe | |
---|---|---|---|---|---|
In core | On disk | ||||
C struct | CSV text file | glibc/stdio | ☑ | ☑ | ☑ (Not all, eg NFS) |
Alternatively: If the language's appends can not guarantee its writes will always append, then note this restriction in the table. If possible, provide an actual code example (possibly using file/record locking) to guarantee correct concurrent appends.
C
Data Representation | IO Library |
Append Possible |
Automatic Append |
Multi-tasking Safe | |
---|---|---|---|---|---|
In core | On disk | ||||
C struct | CSV text file | glibc/stdio | ☑ | ☑ | ☑ (Not all, eg NFS) |
Note: Not all File Systems support atomic appends. In particular NFS does not. It is not known if there is a standard OS independent way of detecting if atomic appends are available. However most Unix & Linux File Systems do support atomic appends, especially for log files.
From a C "struct" to CSV File <lang C>#include <stdio.h>
- include <string.h>
/* note that UID & GID are of type "int" */ typedef const char *STRING; typedef struct{STRING fullname, office, extension, homephone, email; } gecos_t; typedef struct{STRING account, password; int uid, gid; gecos_t gecos; STRING directory, shell; } passwd_t;
- define GECOS_FMT "%s,%s,%s,%s,%s"
- define PASSWD_FMT "%s:%s:%d:%d:"GECOS_FMT":%s:%s"
passwd_t passwd_list[]={
{"jsmith", "x", 1001, 1000, /* UID and GID are type int */ {"Joe Smith", "Room 1007", "(234)555-8917", "(234)555-0077", "jsmith@rosettacode.org"}, "/home/jsmith", "/bin/bash"}, {"jdoe", "x", 1002, 1000, {"Jane Doe", "Room 1004", "(234)555-8914", "(234)555-0044", "jdoe@rosettacode.org"}, "/home/jdoe", "/bin/bash"}
};
main(){ /****************************
- Create a passwd text file *
- /
FILE *passwd_text=fopen("passwd.txt", "w"); int rec_num; for(rec_num=0; rec_num < sizeof passwd_list/sizeof(passwd_t); rec_num++) fprintf(passwd_text, PASSWD_FMT"\n", passwd_list[rec_num]); fclose(passwd_text);
/********************************
- Load text ready for appending *
- /
passwd_text=fopen("passwd.txt", "a+"); char passwd_buf[BUFSIZ]; /* warning: fixed length */ passwd_t new_rec = {"xyz", "x", 1003, 1000, /* UID and GID are type int */ {"X Yz", "Room 1003", "(234)555-8913", "(234)555-0033", "xyz@rosettacode.org"}, "/home/xyz", "/bin/bash"}; sprintf(passwd_buf, PASSWD_FMT"\n", new_rec);
/* An atomic append without a file lock,
Note: wont work on some file systems, eg NFS */ write(fileno(passwd_text), passwd_buf, strlen(passwd_buf)); close(passwd_text);
/***********************************************
- Finally reopen and check record was appended *
- /
passwd_text=fopen("passwd.txt", "r"); while(!feof(passwd_text)) fscanf(passwd_text, "%[^\n]\n", passwd_buf, "\n"); if(strstr(passwd_buf, "xyz")) printf("Appended record: %s\n", passwd_buf);
}</lang>
- Output:
Appended record: xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
Go
<lang go>package main
import (
"bytes" "fmt" "io" "io/ioutil" "os"
)
type pw struct {
account, password string uid, gid uint gecos directory, shell string
}
type gecos struct {
fullname, office, extension, homephone, email string
}
func (p *pw) encode(w io.Writer) (int, error) {
return fmt.Fprintf(w, "%s:%s:%d:%d:%s,%s,%s,%s,%s:%s:%s\n", p.account, p.password, p.uid, p.gid, p.fullname, p.office, p.extension, p.homephone, p.email, p.directory, p.shell)
}
// data cut and pasted from task description var p2 = []pw{
{"jsmith", "x", 1001, 1000, gecos{"Joe Smith", "Room 1007", "(234)555-8917", "(234)555-0077", "jsmith@rosettacode.org"}, "/home/jsmith", "/bin/bash"}, {"jdoe", "x", 1002, 1000, gecos{"Jane Doe", "Room 1004", "(234)555-8914", "(234)555-0044", "jdoe@rosettacode.org"}, "/home/jsmith", "/bin/bash"},
}
var pa = pw{"xyz", "x", 1003, 1000, gecos{"X Yz", "Room 1003",
"(234)555-8913", "(234)555-0033", "xyz@rosettacode.org"}, "/home/xyz", "/bin/bash"}
var expected = "xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913," +
"(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash"
const pfn = "mythical"
func main() {
writeTwo() appendOneMore() checkResult()
}
func writeTwo() {
// overwrites any existing file f, err := os.Create(pfn) if err != nil { fmt.Println(err) return } defer func() { if cErr := f.Close(); cErr != nil && err == nil { fmt.Println(cErr) } }() for _, p := range p2 { if _, err = p.encode(f); err != nil { fmt.Println(err) return } }
}
func appendOneMore() {
// file must already exist f, err := os.OpenFile(pfn, os.O_RDWR|os.O_APPEND, 0) if err != nil { fmt.Println(err) return } if _, err := pa.encode(f); err != nil { fmt.Println(err) } if cErr := f.Close(); cErr != nil && err == nil { fmt.Println(cErr) }
}
func checkResult() {
// reads entire file then closes it b, err := ioutil.ReadFile(pfn) if err != nil { fmt.Println(err) return } if string(bytes.Split(b, []byte{'\n'})[2]) == expected { fmt.Println("append okay") } else { fmt.Println("it didn't work") }
}</lang>
- Output:
append okay
Data Representation | IO Library |
Append Possible |
Automatic Append |
Multi-tasking Safe | |
---|---|---|---|---|---|
In core | On disk | ||||
Byte slice. See note 1. | Abstracted. See note 2. | Go standard library. See note 3. | Yes | No. See note 4. | Should be, but see note 5. |
Notes:
- Data arguments for basic IO functions are type []byte. All data being written ultimately gets encoded to []byte. Some higher level functions accept strings and do the conversion as a convenience. The fmt.Fprintf function used in this task encodes basic types directly to an io.Writer. Some packages such as json and xml have marshallers to encode more complex data structures. These features are common to all writes and are not specific to append.
- The device data representation is abstracted by the type os.File and its associated functions and methods. These methods include seeking by linear file position so a typical data representation is mappable to a linear sequence of bytes. That is about all that can be concluded from the capabilites of the os.File type. This linear nature is not specific to append.
- Go's os package provides high-level services which work reasonably the same across all supported operating systems. Currently the list is darwin, freebsd, linux, netbsd, openbsd, windows. It does this with a common OS-independent interface. Operations which cannot be offered accross all operating systems are not in this package. Additional low-level and operating system specific functions are provided in the syscall package. This organization is not specific to append.
- The file position after opening a file is 0 unless os.O_APPEND is specified in the os.OpenFile function.
- The Go documentation says nothing about file operations by multiple concurrent processes or threads. As the operations are expected to work the same across operating systems however, they presumably do what you would expect. It seems os.FileOpen with os.O_RDWR for example, gives exclusive file access (on local file systems anyway) whether os.O_APPEND is specified or not.
Groovy
Solution: <lang groovy>class PasswdRecord {
String account, password, directory, shell int uid, gid SourceRecord source
private static final fs = ':' private static final fieldNames = ['account', 'password', 'uid', 'gid', 'source', 'directory', 'shell'] private static final stringFields = ['account', 'password', 'directory', 'shell'] private static final intFields = ['uid', 'gid']
PasswdRecord(String line = null) { if (!line) return def fields = line.split(fs) if (fields.size() != fieldNames.size()) { throw new IllegalArgumentException( "Passwd record must have exactly ${fieldNames.size()} '${fs}'-delimited fields") } (0..<fields.size()).each { i -> switch (fieldNames[i]) { case stringFields: this[fieldNames[i]] = fields[i]; break case intFields: this[fieldNames[i]] = fields[i] as Integer; break default /* source */: this.source = new SourceRecord(fields[i]); break } } }
@Override String toString() { fieldNames.collect { "${this[it]}${fs}" }.sum()[0..-2] }
}
class SourceRecord {
String fullname, office, extension, homephone, email
private static final fs = ',' private static final fieldNames = ['fullname', 'office', 'extension', 'homephone', 'email']
SourceRecord(String line = null) { if (!line) return def fields = line.split(fs) if (fields.size() != fieldNames.size()) { throw new IllegalArgumentException( "Source record must have exactly ${fieldNames.size()} '${fs}'-delimited fields") } (0..<fields.size()).each { i -> this[fieldNames[i]] = fields[i] } }
@Override String toString() { fieldNames.collect { "${this[it]}${fs}" }.sum()[0..-2] }
}
def appendNewPasswdRecord = {
PasswdRecord pwr = new PasswdRecord().with { p -> (account, password, uid, gid) = ['xyz', 'x', 1003, 1000] source = new SourceRecord().with { s -> (fullname, office, extension, homephone, email) = ['X Yz', 'Room 1003', '(234)555-8913', '(234)555-0033', 'xyz@rosettacode.org'] s } (directory, shell) = ['/home/xyz', '/bin/bash'] p };
new File('passwd.txt').withWriterAppend { w -> w.append(pwr as String) w.append('\r\n') }
}</lang>
Test: <lang groovy>def checkPasswdFile = { it ->
File passwd = new File('passwd.txt') assert passwd.exists()
passwd.eachLine { line -> def pw = new PasswdRecord(line) assert pw && pw instanceof PasswdRecord assert pw.source && pw.source instanceof SourceRecord println pw }
println()
}
println "File contents before new record added" checkPasswdFile()
appendNewPasswdRecord()
println "File contents after new record added" checkPasswdFile()</lang>
Output:
File contents before new record added jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jsmith:/bin/bash File contents after new record added jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jsmith:/bin/bash xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
Icon and Unicon
Works in both languages: <lang unicon>procedure main()
orig := [ "jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash", "jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jsmith:/bin/bash" ] new := [ "xyz:x:1003:1000:X:Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash" ] fName := !open("mktemp","rp") every (f := open(fName,"w")) | write(f,!orig) | close(f) every (f := open(fName,"a")) | write(f,!new) | close(f) every (f := open(fName,"r")) | write(!f) | close(f)
end</lang>
Run:
->arteotf jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jsmith:/bin/bash xyz:x:1003:1000:X:Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash ->
Data Representation | IO Library |
Append Possible |
Automatic Append |
Multi-tasking Safe | |
---|---|---|---|---|---|
In core | On disk | ||||
String | String | built-in | ☑ | ☑ | ☑ (Not all, eg NFS) |
J
<lang j>require'strings ~system/packages/misc/xenos.ijs' record=: [:|: <@deb;._1@(':',]);._2@do bind '0 :0'
passfields=: <;._1':username:password:gid:uid:gecos:home:shell'
passrec=: LF,~ [: }.@;@ , ':';"0 (passfields i. {. ) { a:,~ {:
R1=: passrec record
username: jsmith password: x gid: 1001 uid: 1000 gecos: Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org home: /home/jsmith shell: /bin/bash
)
R2=: passrec record
username: jdoe password: x gid: 1002 uid: 1000 gecos: Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org home: /home/jdoe shell: /bin/bash
)
R3=: passrec record
username: xyz password: x gid: 1003 uid: 1000 gecos: X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org home: /home/xyz shell: /bin/bash
)
passwd=: <'/tmp/passwd.txt' NB. file needs to be writable on implementation machine
(R1,R2) fwrite passwd R3 fappend passwd
assert 1 e. R3 E. fread passwd </lang>
Data Representation | |
---|---|
In core | On disk |
array | literal |
Note that no file locking is needed if this is implemented under windows (since all file writes are atomic across processes -- only one process can have a file open at one time, by default). Note that file locking would be needed under Linux (or unix), but it's not clear which file locking mechanism should be used.
Java
<lang java>import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class RecordAppender {
public static class Record {
String account, password; Integer uid, gid; String[] gecos; String directory, shell;
public Record(String account, String password, int uid, int gid, String gecos[], String directory, String shell) { this.account = account; this.password = password; this.uid = uid; this.gid = gid; this.gecos = gecos; this.directory = directory; this.shell = shell; }
public Record(String line) { String[] token = line.trim().split(":"); if ((token == null) || (token.length < 7)) { throw new IllegalArgumentException(line); } this.account = token[0].trim(); this.password = token[1].trim(); this.uid = Integer.parseInt(token[2].trim()); this.gid = Integer.parseInt(token[3].trim()); this.gecos = token[4].trim().split(","); this.directory = token[5].trim(); this.shell = token[6].trim(); }
public String asLine() { StringBuilder sb = new StringBuilder(); sb.append(account + ":" + password + ":"); sb.append(uid + ":" + gid + ":"); for (int i = 0; i < gecos.length; i++) { sb.append((i == 0 ? "" : ",") + gecos[i].trim()); } sb.append(":" + directory + ":" + shell + "\n"); return sb.toString(); } }
public static void main(String[] args) { File file = null; FileWriter writer = null; BufferedReader br = null; String line = null; Record record = null; try { file = File.createTempFile("_rosetta", ".passwd"); writer = new FileWriter(file);
writer.write(new Record("jsmith", "x", 1001, 1000, new String[] { "Joe Smith", "Room 1007", "(234)555-8917", "(234)555-0077", "jsmith@rosettacode.org" }, "/home/jsmith", "/bin/bash") .asLine());
writer.write(new Record("jdoe", "x", 1002, 1000, new String[] { "Jane Doe", "Room 1004", "(234)555-8914", "(234)555-0044", "jdoe@rosettacode.org" }, "/home/jdoe", "/bin/bash") .asLine());
writer.close();
// Setting the 'append'-Parameter to true writes data // to the end of the file rather than the beginning
writer = new FileWriter(file, true); writer.write(new Record("xyz", "x", 1003, 1000, new String[] { "X Yz", "Room 1003", "(234)555-8913", "(234)555-0033", "xyz@rosettacode.org" }, "/home/xyz", "/bin/bash") .asLine()); writer.close();
br = new BufferedReader(new FileReader(file)); while ((line = br.readLine()) != null) { record = new Record(line); if (record.account.equals("xyz")) { System.out.println("Appended Record: " + record.asLine()); } } br.close();
} catch (IOException e) { System.err.println("Running Example failed: " + e.getMessage()); } finally { try { if (br != null) { br.close(); } } catch (IOException ignored) { } try { if (writer != null) { writer.close(); } } catch (IOException ignored) { } if (file != null) { file.delete(); } } }
}</lang>
- Output:
Appended Record: xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
MATLAB / Octave
<lang Matlab> DS{1}.account='jsmith';
DS{1}.password='x'; DS{1}.UID=1001; DS{1}.GID=1000; DS{1}.fullname='Joe Smith'; DS{1}.office='Room 1007'; DS{1}.extension='(234)555-8917'; DS{1}.homephone='(234)555-0077'; DS{1}.email='jsmith@rosettacode.org'; DS{1}.directory='/home/jsmith'; DS{1}.shell='/bin/bash';
DS{2}.account='jdoe'; DS{2}.password='x'; DS{2}.UID=1002; DS{2}.GID=1000; DS{2}.fullname='Jane Doe'; DS{2}.office='Room 1004'; DS{2}.extension='(234)555-8914'; DS{2}.homephone='(234)555-0044'; DS{2}.email='jdoe@rosettacode.org'; DS{2}.directory='/home/jdoe'; DS{2}.shell='/bin/bash';
function WriteRecord(fid, rec) fprintf(fid,"%s:%s:%i:%i:%s,%s,%s,%s,%s:%s%s\n", rec.account, rec.password, rec.UID, rec.GID, rec.fullname, rec.office, rec.extension, rec.homephone, rec.email, rec.directory, rec.shell); return; end
%% write fid = fopen('passwd.txt','w'); WriteRecord(fid,DS{1}); WriteRecord(fid,DS{2}); fclose(fid);
new.account='xyz'; new.password='x'; new.UID=1003; new.GID=1000; new.fullname='X Yz'; new.office='Room 1003'; new.extension='(234)555-8913'; new.homephone='(234)555-0033'; new.email='xyz@rosettacode.org'; new.directory='/home/xyz'; new.shell='/bin/bash';
%% append fid = fopen('passwd.txt','a+'); WriteRecord(fid,new); fclose(fid);
% read password file fid = fopen('passwd.txt','r'); while ~feof(fid)
printf('%s\n',fgetl(fid));
end; fclose(fid); </lang>
- Output:
jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jdoe/bin/bash xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz/bin/bash
Perl
The program uses flock(2) (or emulation, if flock is not available) to lock the file. <lang Perl>use strict; use warnings;
use Fcntl qw( :flock SEEK_END );
use constant {
RECORD_FIELDS => [qw( account password UID GID GECOS directory shell )], GECOS_FIELDS => [qw( fullname office extension homephone email )], RECORD_SEP => ':', GECOS_SEP => ',', PASSWD_FILE => 'passwd.txt',
};
- here's our three records
my $records_to_write = [
{ account => 'jsmith', password => 'x', UID => 1001, GID => 1000, GECOS => { fullname => 'John Smith', office => 'Room 1007', extension => '(234)555-8917', homephone => '(234)555-0077', email => 'jsmith@rosettacode.org', }, directory => '/home/jsmith', shell => '/bin/bash', }, { account => 'jdoe', password => 'x', UID => 1002, GID => 1000, GECOS => { fullname => 'Jane Doe', office => 'Room 1004', extension => '(234)555-8914', homephone => '(234)555-0044', email => 'jdoe@rosettacode.org', }, directory => '/home/jdoe', shell => '/bin/bash', },
]; my $record_to_append = {
account => 'xyz', password => 'x', UID => 1003, GID => 1000, GECOS => { fullname => 'X Yz', office => 'Room 1003', extension => '(234)555-8913', homephone => '(234)555-0033', email => 'xyz@rosettacode.org', }, directory => '/home/xyz', shell => '/bin/bash',
};
sub record_to_string {
my $rec = shift; my $sep = shift // RECORD_SEP; my $fields = shift // RECORD_FIELDS; my @ary; for my $field (@$fields) { my $r = $rec->{$field}; die "Field '$field' not found" unless defined $r; # simple sanity check push @ary, ( $field eq 'GECOS' ? record_to_string( $r, GECOS_SEP, GECOS_FIELDS ) : $r ); } return join $sep, @ary;
}
sub write_records_to_file {
my $records = shift; my $filename = shift // PASSWD_FILE; open my $fh, '>>', $filename or die "Can't open $filename: $!"; flock( $fh, LOCK_EX ) or die "Can't lock $filename: $!"; # if someone appended while we were waiting... seek( $fh, 0, SEEK_END ) or die "Can't seek $filename: $!" ; print $fh record_to_string($_), "\n" for @$records; flock( $fh, LOCK_UN ) or die "Can't unlock $filename: $!"; # note: the file is closed automatically when function returns (and refcount of $fh becomes 0)
}
- write two records to file
write_records_to_file( $records_to_write );
- append one more record to file
write_records_to_file( [$record_to_append] );
- test
{
use Test::Simple tests => 1;
open my $fh, '<', PASSWD_FILE or die "Can't open ", PASSWD_FILE, ": $!"; my @lines = <$fh>; chomp @lines; ok( $lines[-1] eq
'xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash',
"Appended record: $lines[-1]" );
} </lang>
Output:
1..1 ok 1 - Appended record: xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
File contents:
jsmith:x:1001:1000:John Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jdoe:/bin/bash xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
Data Representation | IO Library |
Append Possible |
Automatic Append |
Multi-tasking Safe | |
---|---|---|---|---|---|
In core | On disk | ||||
Hash table | Text file | PerlIO | ☑ | ☑ | Advisory lock |
Note that flock uses advisory lock; some other program (if it doesn't use flock) can still unexpectedly write to the file.
PicoLisp
<lang PicoLisp>(setq L '((jsmith x 1001 1000
"Joe Smith,Room 1007,(234)555-8917,\ (234)555-0077,jsmith@rosettacode.org" /home/jsmith /bin/bash ) (jdoe x 1002 1000 "Jane Doe,Room 1004,(234)555-8914,\ (234)555-0044,jdoe@rosettacode.org" /home/jsmith /bin/bash ) ) )
(setq A '(xyz x 1003 1000
"X Yz,Room 1003,(234)555-8913,\ (234)555-0033,xyz@rosettacode.org" /home/xyz /bin/bash ) )
(out "mythical"
(for I L (prinl (glue ': I)) ) )
(out "+mythical"
(prinl (glue ': A)) )
(in "mythical"
(do 2 (line)) (println (and (= "xyz" (pack (till ':))) (= 3 (lines "mythical") ) ) ) )
(bye)</lang>
Python
Data Representation | IO Library |
Append Possible |
Automatic Append |
Multi-tasking Safe | |
---|---|---|---|---|---|
In core | On disk | ||||
dict | CSV text file | builtin | ☑ | ☑ | ☒ |
instance | CSV text file | builtin | To do. |
From a "dict" to a CSV File <lang python>#############################
- Create a passwd text file
- note that UID & gid are of type "text"
passwd_list=[
dict(account='jsmith', password='x', UID=1001, GID=1000, # UID and GID are type int GECOS=dict(fullname='Joe Smith', office='Room 1007', extension='(234)555-8917', homephone='(234)555-0077', email='jsmith@rosettacode.org'), directory='/home/jsmith', shell='/bin/bash'), dict(account='jdoe', password='x', UID=1002, GID=1000, GECOS=dict(fullname='Jane Doe', office='Room 1004', extension='(234)555-8914', homephone='(234)555-0044', email='jdoe@rosettacode.org'), directory='/home/jdoe', shell='/bin/bash')
]
passwd_fields="account password UID GID GECOS directory shell".split() GECOS_fields="fullname office extension homephone email".split()
def passwd_text_repr(passwd_rec):
- convert individual fields to string type
passwd_rec["GECOS"]=",".join([ passwd_rec["GECOS"][field] for field in GECOS_fields]) for field in passwd_rec: # convert "int" fields if not isinstance(passwd_rec[field], str): passwd_rec[field]=`passwd_rec[field]` return ":".join([ passwd_rec[field] for field in passwd_fields ])
passwd_text=open("passwd.txt","w") for passwd_rec in passwd_list:
print >> passwd_text,passwd_text_repr(passwd_rec)
passwd_text.close()
- Load text ready for appending
passwd_text=open("passwd.txt","a+") new_rec=dict(account='xyz', password='x', UID=1003, GID=1000,
GECOS=dict(fullname='X Yz', office='Room 1003', extension='(234)555-8913', homephone='(234)555-0033', email='xyz@rosettacode.org'), directory='/home/xyz', shell='/bin/bash')
print >> passwd_text, passwd_text_repr(new_rec) passwd_text.close()
- Finally reopen and check record was appended
passwd_list=list(open("passwd.txt","r")) if "xyz" in passwd_list[-1]:
print "Appended record:",passwd_list[-1][:-1]</lang>
- Output:
Appended record: xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
RapidQ
The easy short solution <lang vb> 'Short solution: Append record and read last record $Include "Rapidq.inc"
dim file as qfilestream dim filename as string dim LogRec as string
'First create our logfile filename = "C:\Logfile2.txt" file.open(filename, fmCreate) file.writeline "jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash" file.writeline "jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jsmith:/bin/bash" file.close
'Append record file.open(filename, fmOpenWrite) file.position = File.size file.writeline "xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash" file.close
'Read last record file.open (filename, fmOpenRead) while not file.EOF
LogRec = File.Readline
wend
showmessage "Appended record: " + LogRec </lang>
- Output:
Appended record: xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
Full solution: create an object to handle all functions <lang vb> 'Full solution: Create an object with all required fields and 'build-in functions to append a record and to read the last record $include "Rapidq.inc"
Type TLogFile extends QObject
Private: file as qfilestream Public: account as string password as string UID as integer GID as integer GECOS.fullname as string GECOS.office as string GECOS.extension as string GECOS.homephone as string GECOS.email as string directory as string shell as string RSep as string GSep as string function AppendRecord(LogFile as string) as integer with This if fileexists(LogFile) then Result = .file.open(LogFile, fmOpenWrite) else Result = .file.open(LogFile, fmCreate) end if .file.position = .file.size .file.writeline .account + .RSep + .password + .RSep + str$(.UID) + .RSep + str$(.GID) + .RSep +_ .GECOS.fullname + .GSep + .GECOS.office + .GSep + .GECOS.extension +_ .GSep + .GECOS.homephone + .GSep + .GECOS.email + .RSep +_ .directory + .RSep + .shell .file.close end with end function Function ReadLastRecord(LogFile as string) as string dim x as integer dim LogRec as string dim GECOSRec as string With This if fileexists(LogFile) then .file.open(LogFile, fmOpenRead) While not .file.eof LogRec = .file.readline Wend .file.close .account = field$(LogRec, .RSep, 1) .password = field$(LogRec, .RSep, 2) .UID = val(field$(LogRec, .RSep, 3)) .GID = val(field$(LogRec, .RSep, 4)) GECOSRec = field$(LogRec, .RSep, 5) .directory = field$(LogRec, .RSep, 6) .shell = field$(LogRec, .RSep, 7) .GECOS.fullname = field$(GECOSRec, .GSep, 1) .GECOS.office = field$(GECOSRec, .GSep, 2) .GECOS.extension = field$(GECOSRec, .GSep, 3) .GECOS.homephone = field$(GECOSRec, .GSep, 4) .GECOS.email = field$(GECOSRec, .GSep, 5) else showmessage "Can't read file " + Logfile
end if end with End function Constructor RSep = ":" GSep = "," End Constructor
end type
'--- Now we can use our LogFile object: dim LogFile as TLogFile
'--- Let's save a record: Set field values Logfile.account = "jsmith" Logfile.password = "X" Logfile.UID = 1001 Logfile.GID = 1000 Logfile.directory = "/home/jsmith" Logfile.shell = "/bin/bash" Logfile.GECOS.fullname = "Joe Smith" Logfile.GECOS.office = "Room 1007" Logfile.GECOS.extension = "(234)555-8917" Logfile.GECOS.homephone = "(234)555-0077" Logfile.GECOS.email = "jsmith@rosettacode.org" '--- And save it to our logfile Logfile.appendrecord("c:\A test.txt")
'--- Let's save the second one: Set field values Logfile.account = "jdoe" Logfile.password = "X" Logfile.UID = 1002 Logfile.GID = 1000 Logfile.directory = "/home/jsmith" Logfile.shell = "/bin/bash" Logfile.GECOS.fullname = "Jane Doe" Logfile.GECOS.office = "Room 1004" Logfile.GECOS.extension = "(234)555-8914" Logfile.GECOS.homephone = "(234)555-0044" Logfile.GECOS.email = "jdoe@rosettacode.org" '--- And save it to our logfile Logfile.appendrecord("c:\A test.txt")
'--- And append the last one: Set field values Logfile.account = "xyz" Logfile.password = "X" Logfile.UID = 1003 Logfile.GID = 1000 Logfile.directory = "/home/xyz" Logfile.shell = "/bin/bash" Logfile.GECOS.fullname = "X Yz" Logfile.GECOS.office = "Room 1003" Logfile.GECOS.extension = "(234)555-8913" Logfile.GECOS.homephone = "(234)555-0033" Logfile.GECOS.email = "xyz@rosettacode.org" '--- And save it to our logfile Logfile.appendrecord("c:\A test.txt")
'--- Read last record: load all CSV fields in our LogFile object props
Logfile.ReadLastRecord("c:\A test.txt")
'--- And simply Read values from the objects properties
Print Logfile.account
Print Logfile.password
Print Logfile.UID
Print Logfile.GID
Print Logfile.directory
Print Logfile.shell
Print Logfile.GECOS.fullname
Print Logfile.GECOS.office
Print Logfile.GECOS.extension
Print Logfile.GECOS.homephone
Print Logfile.GECOS.email
Print ""
input "Press enter to exit:";a$
</lang>
REXX
data representation | I/O library |
append |
automatic append |
multi-tasking safe | |
---|---|---|---|---|---|
in memory | on disk | ||||
strings | text file | builtin | yes | yes | yes |
The data fields for the three records were coded on two statements instead of
continuing them on separate statements for brevity.
<lang rexx>/*REXX pgm: writes two records, close file, appends another record. */
signal on syntax; signal on novalue /*handle REXX program errors. */
tFID='PASSWD.TXT' /*define the name of the out file*/
call lineout tFID /*close the file, just in case; */
/*could be open from calling pgm.*/
call writeRec tFID,, /*append 1st record to the file. */
'jsmith',"x", 1001, 1000, 'Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org', "/home/jsmith", '/bin/bash'
call writeRec tFID,, /*append 2nd record to the file. */
'jdoe', "x", 1002, 1000, 'Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org', "/home/jsmith", '/bin/bash'
call lineout fid /*close the file. */
call writeRec tFID,, /*append 3rd record to the file. */
'xyz', "x", 1003, 1000, 'X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org', "/home/xyz", '/bin/bash'
/*─account─pw────uid───gid──────────────fullname,office,extension,homephone,Email────────────────────────directory───────shell──*/
call lineout fid /*safe programming: close file. */ exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────WRITEREC subroutine─────────────────*/ writeRec: parse arg fid,_ /*get the fileID, and the 1st arg*/ sep=':' /*field delimiter used in file. */
/*Note: SEP field should be ··· */ /*··· unique and can be any size.*/ do i=3 to arg() /*get each argument and append it*/ _=_ ||sep|| arg(i) /*to the prev. arg, with a : sep.*/ end /*i*/
do tries=0 for 11 /*keep trying for 66 seconds. */ r=lineout(fid,_) /*write (append) the new record. */ if r==0 then return /*Zero? Then record was written.*/ call sleep tries /*Error: so try again after delay*/ end /*tries*/ /*Note: not all REXXes have SLEEP*/
/*possibly: no write access, */ /* proper authority, */ /* permission, etc. */
call err r 'record's(r) "not written to file" fid exit 13 /*───────────────────────────────error handling subroutines and others.─*/ err: say; say; say center(' error! ',40,"*"); say
do j=1 for arg(); say arg(j); say; end; say; exit 13
novalue: syntax: call err 'REXX program' condition('C') "error",,
condition('D'),'REXX source statement (line' sigl"):",, sourceline(sigl)
s: if arg(1)==1 then return arg(3);return word(arg(2) 's',1)</lang> PASSWD.TXT file before the REXX program ran:
:::::::::::::::::::::::::::::::::::::::: Hopefully, this will be considered a comment.
PASSWD.TXT file after the REXX program ran:
:::::::::::::::::::::::::::::::::::::::: Hopefully, this will be considered a comment. jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jsmith:/bin/bash xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
Ruby
Data Representation | IO Library |
Append Possible |
Automatic Append |
Multi-tasking Safe | |
---|---|---|---|---|---|
In core | On disk | ||||
objects (subclass of Struct builtin) | text file | builtin | ☑ | ☑ | ☒ |
<lang ruby>Gecos = Struct.new :fullname, :office, :extension, :homephone, :email class Gecos
def to_s "%s,%s,%s,%s,%s" % [fullname, office, extension, homephone, email] end
end
- Another way define 'to_s' method
Passwd = Struct.new(:account, :password, :uid, :gid, :gecos, :directory, :shell) do
def to_s to_a.map(&:to_s).join(':') end
end
jsmith = Passwd.new('jsmith','x',1001, 1000, Gecos.new('Joe Smith', 'Room 1007', '(234)555-8917', '(234)555-0077', 'jsmith@rosettacode.org'), '/home/jsmith', '/bin/bash') jdoe = Passwd.new('jdoe','x',1002, 1000, Gecos.new('Jane Doe', 'Room 1004', '(234)555-8914', '(234)555-0044', 'jdoe@rosettacode.org'), '/home/jdoe', '/bin/bash') xyz = Passwd.new('xyz','x',1003, 1000, Gecos.new('X Yz', 'Room 1003', '(234)555-8913', '(234)555-0033', 'xyz@rosettacode.org'), '/home/xyz', '/bin/bash')
filename = 'append.records.test'
- create the passwd file with two records
File.open(filename, 'w') do |io|
io.puts jsmith io.puts jdoe
end
puts "before appending:" puts File.readlines(filename)
- append the third record
File.open(filename, 'a') do |io|
io.puts xyz
end
puts "after appending:" puts File.readlines(filename)</lang>
- Output:
before appending: jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jdoe:/bin/bash after appending: jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jdoe:/bin/bash xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
Scala
<lang scala>import java.io.{File, FileWriter, IOException}
import scala.io.Source
object RecordAppender extends App {
val rawDataIt = Source.fromString(rawData).getLines()
def writeStringToFile(file: File, data: String, appending: Boolean = false) = using(new FileWriter(file, appending))(_.write(data))
def using[A <: {def close() : Unit}, B](resource: A)(f: A => B): B = try f(resource) finally resource.close()
def rawData = """jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash |jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jdoe:/bin/bash |xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash""".stripMargin
case class Record(account: String, password: String, uid: Int, gid: Int, gecos: Array[String], directory: String, shell: String) { def asLine: String = s"$account:$password:$uid:$gid:${gecos.mkString(",")}:$directory:$shell\n" }
object Record { def apply(line: String): Record = { val token = line.trim.split(":") require((token != null) || (token.length == 7)) this(token(0).trim, token(1).trim, Integer.parseInt(token(2).trim), Integer.parseInt(token(3).trim), token(4).split(","), token(5).trim, token(6).trim) } }
try { val file = File.createTempFile("_rosetta", ".passwd") using(new FileWriter(file))(writer => rawDataIt.take(2).foreach(line => writer.write(Record(line).asLine)))
writeStringToFile(file, Record(rawDataIt.next()).asLine, appending = true) // Append a record
Source.fromFile(file).getLines().foreach(line => { if (line startsWith """xyz""") print(s"Selected record: ${Record(line).asLine}") }) scala.compat.Platform.collectGarbage() // JVM Windows related bug workaround JDK-4715154 file.deleteOnExit() } catch { case e: IOException => println(s"Running Example failed: ${e.getMessage}") }
} // 57 lines</lang>
Tcl
Data Representation | IO Library |
Append Possible |
Automatic Append |
Multi-tasking Safe | |
---|---|---|---|---|---|
In core | On disk | ||||
nested lists | Colon/Comma-separated text file | builtin | ☑ | ☑ | ☑ |
Note that appending is only safe on POSIX OSes where the data is written in “small enough” amounts to a local disk. This is a limitation of the OS APIs. <lang tcl># Model the data as nested lists, as that is a natural fit for Tcl set basicRecords {
{
jsmith x 1001 1000 { {Joe Smith} {Room 1007} (234)555-8917 (234)555-0077 jsmith@rosettacode.org } /home/jsmith /bin/bash
} {
jdoe x 1002 1000 { {Jane Doe} {Room 1004} (234)555-8914 (234)555-0044 jdoe@rosettacode.org } /home/jsmith /bin/bash
}
} set addedRecords {
{
xyz x 1003 1000 { {X Yz} {Room 1003} (234)555-8913 (234)555-0033 xyz@rosettacode.org } /home/xyz /bin/bash
}
}
proc printRecords {records fd} {
fconfigure $fd -buffering none foreach record $records {
lset record 4 [join [lindex $record 4] ","] puts -nonewline $fd [join $record ":"]\n
}
} proc readRecords fd {
set result {} foreach line [split [read $fd] "\n"] {
if {$line eq ""} continue set record [split $line ":"] # Special handling for GECOS lset record 4 [split [lindex $record 4] ","] lappend result $record
} return $result
}
- Write basic set
set f [open ./passwd w] printRecords $basicRecords $f close $f
- Append the extra ones
- Use {WRONLY APPEND} on Tcl 8.4
set f [open ./passwd a] printRecords $addedRecords $f close $f
set f [open ./passwd] set recs [readRecords $f] close $f
puts "last record is for [lindex $recs end 0], named [lindex $recs end 4 0]"</lang>
- Output:
last record is for xyz, named X Yz
The file will have this content:
jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jsmith:/bin/bash xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
UNIX Shell
Data Representation | IO Library |
Append Possible |
Automatic Append |
Multi-tasking Safe | |
---|---|---|---|---|---|
In core | On disk | ||||
one-dimensional arrays (indexed or associative) | text file | builtin (shell redirections) | ☑ | ☑ | OS defined |
<lang bash>rec1=(
jsmith x 1001 1000 "Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org" /home/jsmith /bin/bash
)
rec2=(
jdoe x 1002 1000 "Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org" /home/jdoe /bin/bash
)
rec3=(
xyz x 1003 1000 "X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org" /home/xyz /bin/bash
)
filename=./passwd-ish
- use parentheses to run the commands in a subshell, so the
- current shell's IFS variable is not changed
(
IFS=: echo "${rec1[*]}" echo "${rec2[*]}"
) > "$filename"
echo before appending: cat "$filename"
- appending, use the ">>" redirection symbol
IFS=: echo "${rec3[*]}" >> "$filename"
echo after appending: cat "$filename"</lang>
- Output:
before appending: jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jdoe:/bin/bash after appending: jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jdoe:/bin/bash xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash
zkl
Data Representation | IO Library |
Append Possible |
Automatic Append |
Multi-tasking Safe | |
---|---|---|---|---|---|
In core | On disk | ||||
classes | text file | libc | ☑ | ☑ | OS defined |
<lang zkl>var [const]
gnames=T("fullname","office","extension","homephone","email"), pnames=T("account","password","uid","gid","gecos","directory","shell");
class Gecos{
var fullname, office, extension, homephone, email; fcn init(str){ gnames.zipWith(setVar,vm.arglist) } fcn toString { gnames.apply(setVar).concat(",") }
} class Passwd{
var account,password,uid,gid,gecos,directory,shell; fcn init(str){ pnames.zipWith(setVar,vm.arglist) } fcn toString { pnames.apply(setVar).concat(":") }
}</lang> The class setVar method takes one or two parameters. With two, it sets the named class variable to a value; with one, it gets the var. If there aren't enough parameters, the missing ones with be set to Void (yeah, poor error checking). <lang zkl>fcn strToPasswd(str){ // blow apart file line to class
p:=str.strip().split(":"); g:=Gecos(p[4].split(",").xplode()); Passwd(p[0,4].xplode(),g,p[5,*].xplode());
}</lang> The List xplode method pushes the list contents to the parameter list. <lang zkl>jsmith:=Passwd("jsmith","x",1001, 1000, Gecos("Joe Smith", "Room 1007", "(234)555-8917", "(234)555-0077", "jsmith@rosettacode.org"), "/home/jsmith", "/bin/bash"); jdoe:=strToPasswd("jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,"
"jdoe@rosettacode.org:/home/jdoe:/bin/bash");
xyz:=strToPasswd("xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,"
"xyz@rosettacode.org:/home/xyz:/bin/bash");
filename:="append.records.test"; f:=File(filename,"w"); // create file with 2 records f.writeln(jsmith,"\n",jdoe); f.close();
f:=File(filename,"a+"); // append a third record f.writeln(xyz); f.close();
File(filename).read().text.print(); // print file</lang>
- Output:
jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8917,(234)555-0077,jsmith@rosettacode.org:/home/jsmith:/bin/bash jdoe:x:1002:1000:Jane Doe,Room 1004,(234)555-8914,(234)555-0044,jdoe@rosettacode.org:/home/jdoe:/bin/bash xyz:x:1003:1000:X Yz,Room 1003,(234)555-8913,(234)555-0033,xyz@rosettacode.org:/home/xyz:/bin/bash