c++ seekg() read specific number - c++

I have a text file:
1 2 3 4 5 6 7 8 9
I want to read a specific number by using seekg()
ifstream fr("duomenys.txt");
fr.seekg(2,ios::beg);
int n;
fr >> n;
cout << n;
If I set position to 1, 2 or 3, readed number is correct, but using other positions, it returns earlier numbers, for example positions 3 and 4 both return 3, positions 5 and 6 both return 4. If I'm right, this is happening because of the spaces between numbers, but I don't get it, why if position is 3, it returns number 3, not 2 as expected because of the space between 2 and 3? And a question, how should I make my function, that I can get specific number from the file using his position? I'm trying to do like an array, which is in a file, for example, A[i] returns item from array at index i, so my function(i) should return item at index i from file.

The simple answer is that you can't. In a text file, the
arguement to seekg must be a value returned from a previous
tellg, with a few exceptions to allow you to seek to the
beginning or the end of the file. Seeking to position 5, for
example, has no meaning.
If you open the file in binary, you can do a little bit more,
but the seek position will still be in bytes; you have to know
the exact position of each number in the file. In the file
you've presented, the positions would be 0, 2, 4... etc. But
this assumes exactly one digit per number, and exactly one space
separating them, which may not be realistic.
For small data sets like yours (in the example, at least), the
obvious solution is just to read all of the data into an
std::vector, and index into that. For very large data sets:
the file must be written expressedly to support the reads, in
binary mode, with a fixed number of characters per number
(including any separators). You could then seek to i * n,
where i is the position you want, and n the number of characters
per number. But again, only in binary mode, both reading and
writing.

Related

How to input/output and format data in classic Fortran?

I'm an enthusiast for retro stuff and I was checking out the first manuals of Fortran. After investigating seems like the input/output instructions and formatting them has remained relatively similar. There is one example in the manual where it reads N numbers (you specify N and then the N numbers in a punch card), and its written as:
READ 1, N, (A(I), I=1,N)
1 FORMAT (I3/(12F6.2)) `
I understand that, with the format of 1, it grabs N and then the N elements. I understand that I3 stands for a 3 digits integer, and 12F6.2 stands for a repetition of 12 elements of width 6 with 2 decimals. However, I don't really get what the slash between I3 and 12F6.2 does. Also, I'm not sure how to input the data from the command line. I know I could simply use READ(,) but I'd really like to learn how to format first. I've changed the floating point from 12F6.2 to 12F6.0 so that I can just type integers.
Also, I would really appreciate why there's a 12 in front of F6.2. I know it repeats the input data, but if I wanted to type 15 numbers, would it still work since I only specified the first 12? I'm quite confused with the syntax. Thanks in advance!
I've tried everything! From writing the data as 3 2 5 6 (N=3, elements 2, 5, 6) to 3, 2, 5, 6, to first type 3, then ENTER, and then the numbers, to change the 12F6.0 to I3 (which doesn't work)...

Program is not accessing correct index of array.. Why?

I come to this site in need of help, after struggling with this problem for a few days now. I am trying to program a poem that accepts some data from standard input and then outputs a poem based on that data.
The code seems to be working, but it is not correct! It is giving me the wrong index of the array I am using. I would love extra eyes to help me with my code and let me know what I am doing wrong.
ALSO! For some reason, I am not able to access the third array of the char array... I tried to place "SIZE - 1" in there but it prints nothing... Would love to understand why this is. Does this look right?
// Program that accepts some data from standard input,
#include <iostream>
#include <cstring>
//here... extracted.
for (int sign = 0; sign < poem[line]; sign++)
{
if (line > word_count)
{
std::cout << " ";
print_poem(seed);
}
else
{
print_poem(seed);
}
You haven't mentioned what exactly the task is but I can at least explain to you parts of the problem.
Are the correct syllables being printed?
Let's assess if the correct syllables are being printed. I ran your code on my machine (with the input you provided that is "100 3 1 5 7 5") and got:
nahoewachi
tetsunnunoyasa
munahohuke
The syllable count of each line is fine (5,7,5) so that's not a problem.
The first syllable you have a problem with is chi in nahoewachi. I'm only illustrating why this syllable is being printed. You can apply the same logic to the rest.
Initially, the seed is 100. Before processing the first row, you apply generate_prnd, which gives 223. Before calculating chi, you print 4 other syllables (na, ho, e and wa). This means that you have applied generate_prnd 8 times before calculating the fifth syllable.
Applying generate_prnd 8 times on 223 gives 711. Applying one more time (to get row) gives 822.
822%9 = 3rd row (0 indexed).
Applying one more time (to get column) gives 361. 361%5 = 1st column.
Therefore the index for the fifth syllable is (3,1). The string at the (3,1)th index is "chi". Therefore, the correct syllable is being printed. The indexing is correct. There's a problem with your logic if you want a different syllable to be printed.
Now, let's assess why there aren't any spaces in your output.
In the example you provided, num_lines=3. The word_counts (actually syllable counts) are 5, 7 and 5. You are applying a space when line (which is always less than num_lines) is greater than word_count.
However, line is always less than word_count since the maximum value of line is 2 (num_lines - 1). Therefore, a space will never be printed.
P.S. If you are allocating memory using new, don't forget to deallocate using delete later.

Controlling newlines when writing out arrays in Fortran

So I have some code that does essentially this:
REAL, DIMENSION(31) :: month_data
INTEGER :: no_days
no_days = get_no_days()
month_data = [fill array with some values]
WRITE(1000,*) (month_data(d), d=1,no_days)
So I have an array with values for each month, in a loop I fill the array with a certain number of values based on how many days there are in that month, then write out the results into a file.
It took me quite some time to wrap my head around the whole 'write out an array in one go' aspect of WRITE, but this seems to work.
However this way, it writes out the numbers in the array like this (example for January, so 31 values):
0.00000 10.0000 20.0000 30.0000 40.0000 50.0000 60.0000
70.0000 80.0000 90.0000 100.000 110.000 120.000 130.000
140.000 150.000 160.000 170.000 180.000 190.000 200.000
210.000 220.000 230.000 240.000 250.000 260.000 270.000
280.000 290.000 300.000
So it prefixes a lot of spaces (presumably to make columns line up even when there are larger values in the array), and it wraps lines to make it not exceed a certain width (I think 128 chars? not sure).
I don't really mind the extra spaces (although they inflate my file sizes considerably, so it would be nice to fix that too...) but the breaking-up-lines screws up my other tooling. I've tried reading several Fortran manuals, but while some of the mention 'output formatting', I have yet to find one that mentions newlines or columns.
So, how do I control how arrays are written out when using the syntax above in Fortran?
(also, while we're at it, how do I control the nr of decimal digits? I know these are all integer values so I'd like to leave out any decimals all together, but I can't change the data type to INTEGER in my code because of reasons).
You probably want something similar to
WRITE(1000,'(31(F6.0,1X))') (month_data(d), d=1,no_days)
Explanation:
The use of * as the format specification is called list directed I/O: it is easy to code, but you are giving away all control over the format to the processor. In order to control the format you need to provide explicit formatting, via a label to a FORMAT statement or via a character variable.
Use the F edit descriptor for real variables in decimal form. Their syntax is Fw.d, where w is the width of the field and d is the number of decimal places, including the decimal sign. F6.0 therefore means a field of 6 characters of width with no decimal places.
Spaces can be added with the X control edit descriptor.
Repetitions of edit descriptors can be indicated with the number of repetitions before a symbol.
Groups can be created with (...), and they can be repeated if preceded by a number of repetitions.
No more items are printed beyond the last provided variable, even if the format specifies how to print more items than the ones actually provided - so you can ask for 31 repetitions even if for some months you will only print data for 30 or 28 days.
Besides,
New lines could be added with the / control edit descriptor; e.g., if you wanted to print the data with 10 values per row, you could do
WRITE(1000,'(4(10(F6.0,:,1X),/))') (month_data(d), d=1,no_days)
Note the : control edit descriptor in this second example: it indicates that, if there are no more items to print, nothing else should be printed - not even spaces corresponding to control edit descriptors such as X or /. While it could have been used in the previous example, it is more relevant here, in order to ensure that, if no_days is a multiple of 10, there isn't an empty line after the 3 rows of data.
If you want to completely remove the decimal symbol, you would need to rather print the nearest integers using the nint intrinsic and the Iw (integer) descriptor:
WRITE(1000,'(31(I6,1X))') (nint(month_data(d)), d=1,no_days)

How to read a text file from the last line in C++? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have a text file like this:
1 0.05 -7
2 0.1 -4
3 0.11 -1
4 0.12 -3
3 0.15 -2
4 0.2 0
5 0.25 8
What I am looking for is to read the text file in C++ from the last line, reach the line which starts with 4, save the values of this line in an array, then continue to reach the line, which starts with 2 and again save the values corresponding to this line.
It is very important for me to start from the last line, since I look for the last updated values for time step 4.
Reading from the beginning gives me this vector 4 0.12 -3 while I am looking for this vector 4 0.2 0
There's really no trivial way to do this. Unless the file is absolutely enormous (100MGB or more), the best approach is probably to simply read the entire content into a vector, and process it in memory [in whatever order you choose, as you can now treat it as a vector of whatever type it is, and thus "random access"].
It is possible to "move backwards from the end", but it's awkward. Something like this would read the entire file from back to front:
ifstream f("somefile.txt");
char c;
// Place get position at one from last.
f.seekg(-1, ios_base::end);
while(f.get(c))
{
... process character ...
// Move back two (because get moved forward one, and
// we want the one before that.
f.seekg(-2, ios_base::cur);
}
You then have to bear in mind that your string comes in backwards, so you have to prepend instead of append.
My strategy for a spot operation would be scan all the file line by line keeping an array of two lines in memory, updated at each incoming line in order to contain always the latest two lines. At EOF your array contains the interested lines.
Pseudocode:
array[2];
array[0]=array[1]=null;
while(!eof)
{
array[1]=array[0];
array[0]=readline();
}
... do what you want with array[1] and array[0]
array[0] will contain the last line of your file, array[1] the one but last.
check for null: if the file contains just one line, or no lines at all, you can have one or both the entries in the array pointing to null.
Of course this is time expensive if the file is very big in size, but it's a simple reliable solution for a spot operation with an order of magnitude of at least some minutes between each operations.
Otherwise, if more frequent polling is needed, why dont continuosly monitor the file?
This is kind a strategy used by tail -f programs.
Jumping around in a text file is difficult and usually highly inefficient. I suggest the following:
Load the whole file into memory, keeping only the most recent result:
ifstream fin("fname.txt");
// ... Don't forget error checking.
std::unordered_map<int, std::pair<float, int>> entries;
int first, third;
float second;
while (fin >> first >> second >> third) {
entries[first] = make_pair(second, third); // Will overwrite existing.
}
Now you can easily access the last entry simply by accessing the map for the key you are looking for.
cout << entries[4]->first << " " << entries[4]->second << endl;
Will output: 0.2 0
Two strategies come to mind:
1-The previous proposed: have array of size 2 or two variables, etc..., to save last two lines read (should work very well if the file is small, and is really straight forward)
2-Using ifstream::seekg(std::ios_base::end) to positionated in the EOF, and search backwards for EOL markers (this should work if file is big and lines are small compared to file). With peek could check for EOL markers (this is more complex taking into account different EOL markers (\n\r, \n, \r), and possibly slow if lines are to long.

Adding specific record to binary file c++

Suppose I have a binary file and text file of all record workers.
The default total month hours are all set to 0.
How to I actually access to the particular month in the binary and change it to the desired value?
This is in text file format
ID Name J F M
1 Jane 0 0 0
2 Mark 0 0 0
3 Kelvin 0 0 0
to
ID Name J F M
1 Jane 0 0 25
2 Mark 0 0 30
3 Kelvin 0 0 40
The 25 is actually the amount of hours worked in march.
I think the first question here is what you mean by "binary". Are you showing the format of the file literally? In other words, at input, is the character going to be '0' or '\0'? When you're done, do you want the file to contain the two digits '3' and '0' or the single character '\25', '\30' or '\40'?
If you're dealing with a single character at a known offset in each record for input, and want to replace it by a single character for the result, things are pretty easy: seek to the right offset in the file, write a byte, seek to the next offset, and continue 'til you've updated all the records.
If the input file contains character strings, so when you update the value its length will (probably) change, then you're pretty much stuck with reading data in, modifying it in memory, and writing the new data back out (usually to a new file). This is pretty easy too, but can be slow if your file is large.
If you're doing this in a real program, I'd think twice about doing it on your own at all. I'd consider using something like SQLite to handle the data instead. This not only allows you to simplify your code, but also makes life quite a bit nicer for your clients. It uses a known/documented file format, so other tools can work with the data, do backups, etc. It supports transactions, logging, roll-backs, etc. In short, they get a robust solution instead of yet another fragile problem.
A file is a stream of bytes. You can access a file by using the c family of functions fopen fread fwrite. Or though c++ iostream operations. In each case you will need to find the record usually by knowing its position and then reading and writing that record. If the records are not of fixed size you will have to handle moving all subsequent records.