开发者

Read text file step-by-step

开发者 https://www.devze.com 2023-04-10 17:35 出处:网络
I have a file which has text like this: #1#14#ADEADE#CAH0F#0#0..... I need to create a code that will find text that follows # symbol, store it to variable an开发者_运维知识库d then writes it to fi

I have a file which has text like this:

#1#14#ADEADE#CAH0F#0#0.....

I need to create a code that will find text that follows # symbol, store it to variable an开发者_运维知识库d then writes it to file WITHOUT # symbol, but with a space before. So from previous code I will get:

1 14 ADEADE CAH0F 0 0......

I first tried to did it in Python, but files are really big and it takes a really huge time to process file, so I decided to write this part in C++. However, I know nothing about C++ regex, and I'm looking for help. Could you, please, recommend me an easy regex library (I don't know C++ very well) or the well-documented one? It would be even better, if you provide a small example (I know how to perform transmission to file, using fstream, but I need help with how to read file as I said before).


This looks like a job for std::locale and his trusty sidekick imbue:

#include <locale>
#include <iostream>


struct hash_is_space : std::ctype<char> {
  hash_is_space() : std::ctype<char>(get_table()) {}
  static mask const* get_table()
  {
    static mask rc[table_size];
    rc['#'] = std::ctype_base::space;
    return &rc[0];
  }
};

int main() {
  using std::string;
  using std::cin;
  using std::locale;

  cin.imbue(locale(cin.getloc(), new hash_is_space));

  string word;
  while(cin >> word) {
    std::cout << word << " ";
  }
  std::cout << "\n";
}


IMO, C++ is not the best choice for your task. But if you have to do it in C++ I would suggest you have a look at Boost.Regex, part of the Boost library.


If you are on Unix, a simple sed 's/#/ /' <infile >outfile would suffice.

Sed stands for 'stream editor' (and supports regexes! whoo!), so it would be well-suited for the performance that you are looking for.


Alright, I'm just going to make this an answer instead of a comment. Don't use regex. It's almost certainly overkill for this task. I'm a little rusty with C++, so I'll not post any ugly code, but essentially what you could do is parse the file one character at a time, putting anything that wasn't a # into a buffer, then writing it out to the output file along with a space when you do hit a #. In C# at least two really easy methods for solving this come to mind:

StreamReader fileReader = new StreamReader(new FileStream("myFile.txt"),
                              FileMode.Open);
string fileContents = fileReader.ReadToEnd();
string outFileContents = fileContents.Replace("#", " ");
StreamWriter outFileWriter = new StreamWriter(new FileStream("outFile.txt"),
                                 Encoding.UTF8);
outFileWriter.Write(outFileContents);
outFileWriter.Flush();

Alternatively, you could replace

string outFileContents = fileContents.Replace("#", " ");

With

StringBuilder outFileContents = new StringBuilder();
string[] parts = fileContents.Split("#");
foreach (string part in parts)
{
    outFileContents.Append(part);
    outFileContents.Append(" ");
}

I'm not saying you should do it either of these ways or my suggested method for C++, nor that any of these methods are ideal - I'm just pointing out here that there are many many ways to parse strings. Regex is awesome and powerful and may even save the day in extreme circumstances, but it's not the only way to parse text, and may even destroy the world if used for the wrong thing. Really.

If you insist on using regex (or are forced to, as in for a homework assignment), then I suggest you listen to Chris and use Boost.Regex. Alternatively, I understand Boost has a good string library as well if you'd like to try something else. Just look out for Cthulhu if you do use regex.


You've left out one crucial point: if you have two (or more) consecutive #s in the input, should they turn into one space, or the same number of spaces are there are #s?

If you want to turn the entire string into a single space, then @Rob's solution should work quite nicely.

If you want each # turned into a space, then it's probably easiest to just write C-style code:

#include <stdio.h>

int main() { 
    int ch;
    while (EOF!=(ch=getchar()))
        if (ch == '#')
            putchar(' ');
        else
            putchar(ch);
    return 0;
}


So, you want to replace each ONE character '#' with ONE character ' ' , right ?

Then it's easy to do since you can replace any portion of the file with string of exactly the same length without perturbating the organisation of the file.
Repeating such a replacement allows to make transformation of the file chunk by chunk; so you avoid to read all the file in memory, which is problematic when the file is very big.

Here's the code in Python 2.7 .

Maybe, the replacement chunk by chunk will be unsifficient to make it faster and you'll have a hard time to write the same in C++. But in general, when I proposed such codes, it has increased the execution's time satisfactorily.

def treat_file(file_path, chunk_size):
    from os import fsync

    from os.path import getsize
    file_size = getsize(file_path)

    with open(file_path,'rb+') as g:
        fd = g.fileno() # file descriptor, it's an integer

        while True:
            x = g.read(chunk_size)
            g.seek(- len(x),1)
            g.write(x.replace('#',' '))
            g.flush()
            fsync(fd)
            if g.tell() == file_size:
                break

Comments:

open(file_path,'rb+')

it's absolutely obligatory to open the file in binary mode 'b' to control precisely the positions and movements of the file's pointer;
mode '+' is to be able to read AND write in the file

fd = g.fileno()

file descriptor, it's an integer

x = g.read(chunk_size)

reads a chunk of size chunk_size . It would be tricky to give it the size of the reading buffer, but I don't know how to find this buffer's size. Hence a good idea is to give it a power of 2 value.

g.seek(- len(x),1)

the file's pointer is moved back to the position from which the reading of the chunk has just been made. It must be len(x), not chunk_size because the last chunk read is in general less long than chink_size

g.write(x.replace('#',' '))

writes on the same length with the modified chunk

g.flush()
fsync(fd)

these two instructions force the writing, otherwise the modified chunk could remain in the writing buffer and written at uncontrolled moment

if g.tell() >= file_size:  break

after the reading of the last portion of file , whatever is its length (less or equal to chunk_size), the file's pointer is at the maximum position of the file, that is to say file_size and the program must stop

.

In case you would like to replace several consecutive '###...' with only one, the code is easily modifiable to respect this requirement, since writing a shortened chunk doesn't erase characters still unread more far in the file. It only needs 2 files's pointers.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号