So, here's the code of the procedure which reads every structure from file, deletes first-found structure which has an AgreementNo that is equal to the inserted int query. It then shortens the array and rewrites the file.
The problem is, it just shortens the array and deletes the last element - as if the searching criterias are not met, even though they should be.
(Before the procedure starts, the file is opened in a+b mode, so in the end, it is reopened that way.)
void deleteClient(int query, FILE *f){
int filesize = ftell(f);
int n = filesize/sizeof(Client);
Client *c = new Client[n];
Client *c2 = new Client[n-1];
rewind(f);
fread(c, sizeof(Client), n, f);
for(int i=0; i<n; i++){
if(c[i].agreementNo == query ){
c[i] = c[n];
break;
}
}
for (int i=0; i<n-1; i++){ c2[i] = c[i]; } // reduce the size of the array ( -1 extra element)
fclose(f);
remove("Client.dat");
f = fopen("Client.dat", "w+b");
for(int i=0;i<n-1; i++) {
fwrite(&c2[i], sizeof(Client), 1, f);
}
fclose(f);
f = fopen("Client.dat", "a+b");
}
What could be the cause of the described problem? Did I miss something in the code?
I'd do it this way:
struct MatchAgreementNo
{
MatchAgreementNo(int agree) : _agree(agree) {}
bool operator()(const Client& client) { return client.agreementNo == agree; }
};
void deleteClient(int query, FILE *f)
{
int rc = fseek(f, 0, SEEK_END);
assert(rc == 0);
long filesize = ftell(f);
int n = filesize / sizeof(Client);
assert(filesize % sizeof(Client) == 0);
Client *begin = mmap(NULL, filesize, PROT_READ|PROT_WRITE,
MAP_SHARED, fileno(f), 0);
assert(begin != MAP_FAILED);
Client *end = std::remove_if(begin, begin + n, MatchAgreementNo(query));
rc = ftruncate(fileno(f), (end - begin) * sizeof(Client));
assert(rc == 0);
munmap(begin, filesize);
}
That is, define a predicate function which does the query you want. Memory-map the entire file, so that you can apply STL algorithms on what is effectively an array of Clients. remove_if() takes out the element(s) that match (not only the first one), and then we truncate the file (which may be a no-op if nothing was removed).
By writing it this way, the code is a bit higher-level, more idiomatic C++, and hopefully less error-prone. It's probably faster too.
one change needed in your code is to save the index of the first found "bad" entry somewhere, and then copy your original array around that entry. Obviously, if no "bad" entry is found, then you aren't supposed to do anything.
One word of warning: the approach of reading the original file as a whole is only applicable for relatively small files. For the larger files, a better approach would be opening another (temporary) file, reading the original file in chunks and then copying it as you go (and after you found the entry which is skipped just copying the rest of the contents). I guess there is even more space for the optimization here, considering that except for that one entry, the rest of file contents is left unchanged.
void deleteClient(int query, FILE *f){
int filesize = ftell(f);
int n = filesize/sizeof(Client);
int found = -1;
Client *c = new Client[n];
Client *c2 = new Client[n-1];
rewind(f);
fread(c, sizeof(Client), n, f);
for(int i=0; i<n; i++){
if(c[i].agreementNo == query ){
printf("entry No.%d will be deleted\n", i);
found = i;
break;
}
}
if(found == -1) return;
if (i>0) for (int i=0; i<found; i++) { c2[i] = c[i]; } // copy the stuff before the deleted entry if it's >0
for (int i=found+1; i<n; i++){ c2[i-1] = c[i]; } // reduce the size of the array ( -1 extra element)
fclose(f);
remove("Client.dat");
f = fopen("Client.dat", "w+b");
for(int i=0;i<n-1; i++) {
fwrite(&c2[i], sizeof(Client), 1, f);
}
fclose(f);
f = fopen("Client.dat", "a+b");
}
Related
I am using a SHA1 Hash to verify the authenticity of a .wav file. The SHA1 function I am using takes in three parameter:
a pointer to the authentication file with .auth extension
The data buffer read from the .wav file (which must be less than 42000 bytes in size)
The length of the buffer
for (int i = 0; i < size_buffer; i++) {
DataBuffer[i] = fgetc(WavResult);
}
util_sha1_calculate(&AuthContext, DataBuffer, size_buffer);
How can I set a read function to read 42000 bytes, transfer the data to util_sha1_calculate(&AuthContext, DataBuffer, size_buffer), and start from the position it left off when the loop is repeated, and proceed to read then next 42000 bytes?
You can put your shown for loop inside of another outer loop that runs until EOF is reached, eg:
size_t size;
int ch;
while (!feof(WavResult))
{
size = 0;
for (int i = 0; i < size_buffer; i++) {
ch = fgetc(WavResult);
if (ch == EOF) break;
DataBuffer[size++] = (char) ch;
}
if (size > 0)
util_sha1_calculate(&AuthContext, DataBuffer, size);
}
However, you should consider replacing the inner for loop with a single call to fread() instead, eg:
size_t nRead;
while ((nRead = fread(DataBuffer, 1, size_buffer, WavResult)) > 0)
{
util_sha1_calculate(&AuthContext, DataBuffer, nRead);
}
I'm trying to initialize an array of integers dynamically, since the size of the array changes based on input.
The program is as follows:
int main()
{
int* list = createList("dis.bin");
for (int i = 0; i < sizeof(list) / sizeof(int); i++)
{
printf("%d\n", list[i]);
}
}
With createList() function as written:
int* createList(const char* file_name)
{
int counter = 1;
int* inst{};
FILE* myFile = fopen(file_name, "rb");
if (myFile == nullptr)
{
printf("\nFile not opened\n");
return 0;
}
int x = 0;
for (int i = 0; !(feof(myFile)); i++)
{
fread(&x, sizeof(int), 1, myFile);
inst = new int[counter];
inst[i] = x;
printf("%08x #%-4d | Int equiv: %-12d | Bin equiv: %s\n", x, counter, inst[i], ToBinary(inst[i], 0));
counter += 1;
x = 0;
}
return inst;
}
createList reads from a .bin file (basically containing an array of bytes) and inserts each pair of 4 bytes to an item in the array inst. I do this by allocating a new amount of space for the array based on the counter variable. (So whatever value counter is becomes the size of the array with inst = new int[counter]) Then I set the contents of the array at the given index i equal to x (the pair of bytes read) I would assume it is working correctly in createList at least, because of the printf statement which is printing each element in inst[].
However, when I call createList("dis.bin") in main and assign it to the variable int* list, I try to iterate through each value. But this just prints out one uninitialized value (-842150451, if you're curious). So I'm not sure what I'm doing wrong here?
I should mention that I am NOT using vectors or really any std container. I am just working with arrays. I also am using printf for specific reasons.
This question is tagged as C++, but OP is showing C code and says they need it in C, so I will show this in C... but the pre-req is that it uses new and not malloc
int* createList(const char* file_name, int& count)
{
// initialize count, so that way if we return early, we don't have invalid information
count = 0;
// open the file ad "READ" and "BINARY"
FILE* myFile = fopen(file_name, "rb");
if (!myFile)
{
printf("\nFile not opened\n");
return 0;
}
// calculate how many 4-byte integers exist in the file using
// the file length
fseek(myFile, 0, SEEK_END);
count = ftell(myFile) / sizeof(int);
rewind(myFile);
// allocate the memory
int* returnData = new int[count];
// read in 4-byte chunks to our array until it can't read anymore
int i = 0;
while (fread(&returnData[i++], sizeof(int), 1, myFile) == 1);
// close the file
fclose(myFile);
// return our newly allocated data
return returnData;
}
int main()
{
int count;
int* myInts = createList("c:\\users\\andy\\desktop\\dis.bin", count);
for (int i = 0; i < count; ++i) {
printf("%d\n", myInts[i]);
}
// don't forget to delete your data. (another reason a vector would be better suited... no one remembers to delete :)
delete myInts;
}
Two things here:
The usage of new was misinterpreted by me. For whatever reason, I thought that each time I allocated new memory for inst that it would just be appending new memory to the already allocated memory, but this is obviously not the case. If I wanted to simulate this, I would have to copy the contents of the array after each iteration and add that to the newly allocated memory. To solve this, I waited to allocate memory for inst until after the file iteration was complete.
As Andy pointed out, sizeof(list) / sizeof(int) would not give me the number of elements in list, since it is a pointer. To get around this, I created a new parameter int &read for the createList() function in order to pass the number of items created.
With these points, the new function looks like this and works as intended:
int* createList(const char* file_name, int &read)
{
int counter = 1;
FILE* myFile = fopen(file_name, "rb");
if (myFile == nullptr)
{
printf("\nFile not opened\n");
return 0;
}
int x = 0;
for (int i = 0; !(feof(myFile)); i++)
{
fread(&x, sizeof(int), 1, myFile);
printf("%08x #%-4d | Int equiv: %-12d | Bin equiv: %s\n", x, counter, x, ToBinary(x, 0));
counter += 1;
}
int* inst = new int[counter];
read = counter;
rewind(myFile); // rewind to beginning of file
for (int i = 0; !(feof(myFile)); i++)
{
fread(&x, sizeof(int), 1, myFile);
inst[i] = x;
x = 0;
}
return inst;
}
With main changed a bit as well:
int main()
{
int read;
int* list = createList("dis.bin", read);
for (int i = 0; i < read; i++)
{
printf("%d\n", list[i]);
}
}
As for the comments about the invalidity of !(feof(myFile)), although helpful, this was not a part of my question and thus not of my concern. But I will source the solution to that for the sake of spreading important information: Why is "while ( !feof(file) )" always wrong?
I used pthread_create in C++, and the program runs, but in pthread function, the argument result was the same. Why? I have used pthread_mutex_lock, but it is not effective. I can't find out the reason.
The codes below:
Minimal:
const int MAX_THREADS = 5;
class FileCpThread
{
public:
FileCpThread(const string &src,const string & des){
srcFile = src.c_str();
desFile = des.c_str();
}
~FileCpThread(){}
startThreadCopy();
private:
static void *threadCp(void *param);
static int getFileSize(const std::string &filename);
string srcFile;
int num;
string desFile;
};
Complete:
bool FileCpThread::startThreadCopy()
{
pthread_t pid[MAX_THREADS];
pthread_mutex_init(&mutex, NULL);
for (int i = 0; i < MAX_THREADS; i++)
{
pthread_mutex_lock(&mutex);
this->num = i;
pthread_mutex_unlock(&mutex);
pthread_create(&pid[i], NULL, threadCp, (void *)this);
}
for (int j = 0; j < MAX_THREADS; j++)
{
pthread_join(pid[j], NULL);
}
return true;
}
int FileCpThread::getFileSize(const string &filename)
{
struct stat st;
memset(&st, 0, sizeof(st));
stat(filename.c_str(), &st);
return st.st_size;
}
void *FileCpThread::threadCp(void *param)
{
FileCpThread *ft = (FileCpThread *)param;
FILE *fin = fopen(ft->srcFile.c_str(), "r+");
FILE *fout = fopen(ft->desFile.c_str(), "w+");
int size = getFileSize(ft->srcFile.c_str());
pthread_mutex_lock(&ft->mutex);
int number = ft->num;
pthread_mutex_unlock(&ft->mutex);
cout << "number:::" << number << endl;
fseek(fin, size * (number) / MAX_THREADS, SEEK_SET);
fseek(fout, size * (number) / MAX_THREADS, SEEK_SET);
char buff[1024] = {'\0'};
int len = 0;
int total = 0;
while ((len = fread(buff, 1, sizeof(buff), fin)) > 0)
{
fwrite(buff, 1, len, fout);
total += len;
if (total > size / MAX_THREADS)
{
break;
}
}
fclose(fin);
fclose(fout);
}
Verifiable:
bool FileCpThread::startThreadCopy()
pthread_mutex_lock(&ft->mutex);
int number = ft->num;
pthread_mutex_unlock(&ft->mutex);
cout << "number:::" << number << endl;
You get the same value in the thread function because you pass the same value to it each time. Your code is:
for (int i = 0; i < MAX_THREADS; i++)
{
pthread_mutex_lock(&mutex);
this->num = i;
pthread_mutex_unlock(&mutex);
pthread_create(&pid[i], NULL, threadCp, (void *)this);
}
The value of this does not change between iterations, so the same value is passed to threadCp each time it is used.
If your problem is that all the threads see the same value for this->num, then the problem is still that you pass the same pointer to the function each time, but you also have the indeterminacy with which the threads are scheduled, and Sod's Law dictates that the threads won't be activated until the last one is created, so they all see the same value in this->num. You have to make sure that each thread gets independent information to work with — assuming each thread should be getting independent information.
For N threads, you need to create an array of N values — it appears that N int might be sufficient, and (a) set the elements appropriately and (b) pass one element of the array to each thread.
As an element of the class:
int thread_number[MAX_THREADS];
As initialization:
for (int i = 0; i < MAX_THREADS; i++)
this->thread_number[i] = i;
In the call to pthread_create():
pthread_create(&pid[i], NULL, threadCp, &this->thread_number[i]);
In the function threadCp(), instead of:
FileCpThread *ft = (FileCpThread *)param;
use:
int number = *(int *)param;
And you don't need the mutex for synchronization any more (at least, not to control access to the thread number).
If the number of threads is variable, use a vector<int>. If it would be more sensible to compute the split of the file before launching the threads, do that (it probably is sensible).
Do note that having 5 threads accessing separate portions of a single file is probably not going to improve overall performance. As an exercise in coordination between processes, it is fine. As an exercise in improving overall performance, it is probably not a good idea. (Reason: the file will be on a single file system, so the same controllers will be used in accessing the separate sections of disk, leading to head positioning contention. Performance might actually be worse than running single-threaded.)
I am trying to simply write an array of float values to a file and then read it back.
I have tried writing it directly from an array, but when reading it back I keep hitting a problem for arrays with length greater than 153. The code example writes each float value one by one for clarity.
For values with index greater than or equal to 153 they have the value 153.0, where they should be 153.0, 154.0, 155.0, ...
Why doesn't this code work for me?
int length = 160;
char* fileName = "testFile.dat";
// Write data to file
FILE* file = fopen (fileName, "w");
for(int i = 0; i< length; i++){
// We are just storing the indices, so value at i is equal to i
float f = (float) i;
fwrite(&f, sizeof(float), 1, file);
}
fclose(file);
// Read data from file into results array
file = fopen(fileName, "r");
float* results = new float[length];
for(int i = 0; i< length; i++){
float f;
fread(&f, sizeof(float), 1, file);
results[i] = f;
}
fclose(file);
// Now check data in results array
bool fail = false;
for(int i = 0; i< length; i++){
if(results[i]!=(float)i){
fail = true; // This should not be hit, but it is!
}
}
delete [] results;
Thanks,
Dave
FILE* file = fopen (fileName, "wb");
FILE* file = fopen (fileName, "rb");
Given (in C++)
char * byte_sequence;
size_t byte_sequence_length;
char * buffer;
size_t N;
Assuming byte_sequence and byte_sequence_length are initialized to some arbitrary length sequence of bytes (and its length), and buffer is initialized to point to N * byte_sequence_length bytes, what would be the easiest way to replicate the byte_sequence into buffer N times? Is there anything in STL/BOOST that already does something like this?
For example, if the sequence were "abcd", and N was 3, then buffer would end up containing "abcdabcdabcd".
I would probably just go with this:
for (int i=0; i < N; ++i)
memcpy(buffer + i * byte_sequence_length, byte_sequence, byte_sequence_length);
This assumes you are dealing with binary data and are keeping track of the length, not using '\0' termination.
If you want these to be c-strings you'll have to allocate an extra byte and add in the '\0' a the end. Given a c-string and an integer, you'd want to do it like this:
char *RepeatN(char *source, size_t n)
{
assert(n >= 0 && source != NULL);
size_t length = strlen(source) - 1;
char *buffer = new char[length*n + 1];
for (int i=0; i < n; ++i)
memcpy(buffer + i * length, source, length);
buffer[n * length] = '\0';
}
Repeating the buffer while avoiding pointer arithmetic:
You can use std::vector < char > or std::string to make things easier for you. Both of these containers can hold binary data too.
This solution has the nice properties that:
You don't need to worry about memory access violations
You don't need to worry about getting the size of your buffer correct
You can append sequences at any time to your buffer without manual re-allocations
.
//Note this works even for binary data.
void appendSequenceToMyBuffer(std::string &sBuffer
, const char *byte_sequence
, int byte_sequence_length
, int N)
{
for(int i = 0; i < N; ++i)
sBuffer.append(byte_sequence, byte_sequence_length);
}
//Note: buffer == sBuffer.c_str()
Alternate: For binary data using memcpy:
buffer = new char[byte_sequence_length*N];
for (int i=0; i < N; ++i)
memcpy(buffer+i*byte_sequence_length, byte_sequence, byte_sequence_length);
//...
delete[] buffer;
Alternate: For null terminated string data using strcpy:
buffer = new char[byte_sequence_length*N+1];
int byte_sequence_length = strlen(byte_sequence);
for (int i=0; i < N; ++i)
strcpy(buffer+i*byte_sequence_length, byte_sequence, byte_sequence_length);
//...
delete[] buffer;
Alternate: If you are filling the buffer with a single value:
buffer = new char[N];
memset(buffer, byte_value, N);
//...
delete[] buffer;
If N is known to be a power of 2, you can copy from the first part of your buffer to subsequent parts, increasing the amount copied each time:
assert((N > 0) && ((N & (N-1)) == 0));
memcpy(buffer, byte_sequence, byte_sequence_length);
for (size_t i = 1; i < N; i *= 2)
memcpy(buffer + i * byte_sequence_length, buffer, i * byte_sequence_length);
Edit: It is trivial to extend this to work when N is not a power of 2. Here's an improved version, which removes all constraints on N and also replaces the odd for statement with a while.
if (N > 0)
memcpy(buffer, byte_sequence, byte_sequence_length);
size_t copied = 1;
while (copied < N)
{
size_t tocopy = min(copied, N - copied);
memcpy(buffer + copied * byte_sequence_length, buffer, tocopy * byte_sequence_length);
copied += tocopy;
}
You can use the STL algorithm Generate:
MSDN: Generate