Append a record to the end of a text file: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Python}}: fix Python version as per @Rdm's inconsistancy observations.)
m (Update C comments and code, update python table.)
Line 46: Line 46:
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.
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.
=={{header|C}}==
=={{header|C}}==
<!-- ☐ ☑ ☒ -->
{|class="wikitable" style="text-align: center; margin: 1em auto 1em auto;"
{|class="wikitable" style="text-align: center; margin: 1em auto 1em auto;"
|+ Append Capabilities.
|+ Append Capabilities.
Line 58: Line 57:
! In core || On disk
! In core || On disk
|-
|-
| C struct || CSV text file || glibc/stdio || ☑ || ☑ || ☑
| C struct || CSV text file || glibc/stdio || ☑ || ☑ || ☑ (Not all, eg NFS)
|}
|}
'''Note:''' Not all [[wp:File system|File Systems]] support [[wp:Atomicity_(database_systems)#Implementation|atomic appends]]. In particular [[wp:Network File System (protocol)|NFS]] does not. It is not known if there is a standard [[wp:Operating system|OS]] independent way of detecting if atomic appends are available. However most Unix & Linux File Systems do support atomic appends, especially for [[wp:Log file|log files]].

'''From a C "struct" to CSV File'''
'''From a C "struct" to CSV File'''
<lang C>#include <stdio.h>
<lang C>#include <stdio.h>
#include <string.h>
#include <string.h>
/* note that UID & gid are of type "text" */
/* note that UID & GID are of type "int" */
typedef char *STRING;
typedef char *STRING;
typedef struct{STRING fullname, office, extension, homephone, email; } gecos_t;
typedef struct{STRING fullname, office, extension, homephone, email; } gecos_t;
Line 74: Line 75:
{"jsmith", "x", 1001, 1000, /* UID and GID are type int */
{"jsmith", "x", 1001, 1000, /* UID and GID are type int */
{"Joe Smith", "Room 1007", "(234)555-8917", "(234)555-0077", "jsmith@rosettacode.org"},
{"Joe Smith", "Room 1007", "(234)555-8917", "(234)555-0077", "jsmith@rosettacode.org"},
"/home/jsmith", "/bin/sh"},
"/home/jsmith", "/bin/bash"},
{"jdoe", "x", 1002, 1000,
{"jdoe", "x", 1002, 1000,
{"Jane Doe", "Room 1004", "(234)555-8914", "(234)555-0044", "jdoe@rosettacode.org"},
{"Jane Doe", "Room 1004", "(234)555-8914", "(234)555-0044", "jdoe@rosettacode.org"},
"/home/jdoe", "/bin/sh"}
"/home/jdoe", "/bin/bash"}
};
};


Line 94: Line 95:
********************************/
********************************/
passwd_text=fopen("passwd.txt", "a+");
passwd_text=fopen("passwd.txt", "a+");
char passwd_buf[BUFSIZ]; /* warning: fixed length */
passwd_t new_rec =
passwd_t new_rec =
{"xyz", "x", 1003, 1000, /* UID and GID are type int */
{"xyz", "x", 1003, 1000, /* UID and GID are type int */
{"X Yz", "Room 1003", "(234)555-8913", "(234)555-0033", "xyz@rosettacode.org"},
{"X Yz", "Room 1003", "(234)555-8913", "(234)555-0033", "xyz@rosettacode.org"},
"/home/xyz", "/bin/sh"};
"/home/xyz", "/bin/bash"};
fprintf(passwd_text, PASSWD_FMT"\n", new_rec);
sprintf(passwd_buf, PASSWD_FMT"\n", new_rec);
/* An atomic append without a file lock,
fclose(passwd_text);
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 *
* Finally reopen and check record was appended *
***********************************************/
***********************************************/
char passwd_buf[512]; /* warning: fixed length */
passwd_text=fopen("passwd.txt", "r");
passwd_text=fopen("passwd.txt", "r");
while(!feof(passwd_text))
while(!feof(passwd_text))
Line 113: Line 117:
'''Output:'''
'''Output:'''
<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
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>

=={{header|J}}==
=={{header|J}}==


Line 242: Line 245:
! In core || On disk
! In core || On disk
|-
|-
| dict || CSV text file || builtin || ☑ || ☑ ||
| dict || CSV text file || builtin || ☑ || ☑ ||
|-
|-
| instance || CSV text file || builtin || ? || ? || ?
| instance || CSV text file || builtin ||colspan=3| To do.
|}
|}
'''From a "dict" to a CSV File'''
'''From a "dict" to a CSV File'''

Revision as of 10:29, 5 October 2011

Append a record to the end of a text file is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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.

Finally: Provide a summary of the language's "append record" capabilities in a table.

Source record field types and contents.
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
Record to be appended.
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

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

Append Capabilities.
Data Representation IO
Library
Append
Possible
Automatic
Append
Multi-tasking
Guaranteed
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>

  1. include <string.h>

/* note that UID & GID are of type "int" */ typedef 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;

  1. define GECOS_FMT "%s,%s,%s,%s,%s"
  2. 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

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>

Append Capabilities.
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

This example is incomplete. It does not have a table of append record capabilities. Please ensure that it meets all task requirements and remove this message.

<lang java>import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter;

public class Append{ public static void main(String[] args){ try{ System.out.println("Pre-loading file..."); //the "true" here is for autoflush PrintWriter out = new PrintWriter(new FileWriter("passwd"), true); out.println("user1 password1"); out.println("user2 password2"); out.close(); showFile("passwd");

System.out.println("\nAppending new line to file..."); //the first "true" is for append, the second is for autoflush out = new PrintWriter(new FileWriter("passwd", true), true); out.println("user3 password3"); out.close(); showFile("passwd"); }catch(IOException e){ System.err.println("Error in writing file: " + e.getMessage()); } }

private static void showFile(String filename){ try{ BufferedReader in = new BufferedReader(new FileReader(filename)); while(in.ready()){ System.out.println(in.readLine()); } }catch(FileNotFoundException e){ System.err.println("File " + filename + " not found."); }catch(IOException e){ System.err.println("Error in reading file: " + e.getMessage()); } } }</lang> Output:

Pre-loading file...
user1 password1
user2 password2

Appending new line to file...
user1 password1
user2 password2
user3 password3

Python

Append Capabilities.
Data Representation IO
Library
Append
Possible
Automatic
Append
Multi-tasking
Guaranteed
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>#############################

  1. Create a passwd text file
  2. 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):

  1. 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()

  1. 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()

  1. 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