Читать текстовый файл шаг за шагом - PullRequest
1 голос
/ 05 октября 2011

У меня есть файл с таким текстом:

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

Мне нужно создать код, который будет находить текст, следующий за символом #, сохранять его в переменной и затем записывать его в файл без символа #, но с пробелом перед. Так из предыдущего кода я получу:

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

Сначала я попытался сделать это на Python, но файлы действительно большие, и на их обработку уходит очень много времени, поэтому я решил написать эту часть на C ++. Тем не менее, я ничего не знаю о регулярных выражениях C ++, и я ищу помощь. Не могли бы вы порекомендовать мне простую библиотеку регулярных выражений (я не очень хорошо знаю С ++) или хорошо документированную? Было бы еще лучше, если бы вы предоставили небольшой пример (я знаю, как выполнить передачу в файл, используя fstream, но мне нужна помощь с тем, как читать файл, как я уже говорил).

Ответы [ 6 ]

4 голосов
/ 05 октября 2011

Это похоже на работу для std::locale и его верного помощника 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";
}
1 голос
/ 05 октября 2011

Если вы работаете в Unix, достаточно простого sed 's/#/ /' <infile >outfile.

Sed означает «редактор потоков» (и поддерживает регулярные выражения!Вы ищете.

1 голос
/ 05 октября 2011

IMO, C ++ - не лучший выбор для вашей задачи.Но если вам нужно сделать это на C ++, я бы посоветовал вам взглянуть на Boost.Regex , часть библиотеки Boost.

0 голосов
/ 05 октября 2011

Итак, вы хотите заменить каждый ОДИН символ '#' ОДНЫМ символом ' ', верно?

Тогда это легко сделать, поскольку вы можете заменить любую часть файла строкой с одинаковыми значениями.длина без нарушения организации файла.
Повторение такой замены позволяет произвести преобразование фрагмента файла в фрагмент;поэтому вы избегаете чтения всего файла в памяти, что проблематично, если файл очень большой.

Вот код на Python 2.7.

Возможно, замена фрагмента на фрагмент будет неэффективнойчтобы сделать это быстрее, и вам будет трудно писать то же самое на C ++.Но в целом, когда я предлагал такие коды, это удовлетворительно увеличило время выполнения.

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

Комментарии:

open(file_path,'rb+')

абсолютно необходимо открыть файл в двоичном режиме 'b' , чтобы точно контролировать положения и перемещения указателя файла;
mode '+' для возможности чтения и записи в файл

fd = g.fileno()

дескриптор файла, это целое число

x = g.read(chunk_size)

чтениякусок размером chunk_size .Было бы сложно дать ему размер буфера чтения, но я не знаю, как найти размер этого буфера.Следовательно, хорошей идеей является присвоить ему значение 2.

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

указатель файла перемещается обратно в положение, из которого только что было выполнено чтение фрагмента.Это должно быть len(x), а не chunk_size , потому что последнее чтение фрагмента обычно меньше, чем chink_size

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

записи на той же длине с измененнымchunk

g.flush()
fsync(fd)

эти две инструкции форсируют запись, в противном случае измененный кусок может остаться в буфере записи и записаться в неуправляемый момент

if g.tell() >= file_size:  break

после чтения последней части файланезависимо от его длины (меньше или равно chunk_size), указатель файла находится на максимальной позиции файла, то есть file_size , и программа должна остановиться

.

В случае, если вы хотите заменить несколько последовательных '### ...' только на один, код легко модифицируется, чтобы соответствовать этому требованию, так как запись сокращенного фрагмента не стирает символы, которые еще не прочитаны, более далеко вфайл.Для этого нужны только 2 указателя на файлы.

0 голосов
/ 05 октября 2011

Вы упустили один важный момент: если у вас есть два (или более) последовательных # с на входе, они должны превратиться в один пробел, или такое же количество пробелов есть # с?

Если вы хотите превратить всю строку в один пробел, то решение @ Rob должно работать очень хорошо.

Если вы хотите, чтобы каждый # превратился в пробел, то, вероятно, проще всего написать код в стиле C:

#include <stdio.h>

int main() { 
    int ch;
    while (EOF!=(ch=getchar()))
        if (ch == '#')
            putchar(' ');
        else
            putchar(ch);
    return 0;
}
0 голосов
/ 05 октября 2011

Хорошо, я просто собираюсь сделать это ответом вместо комментария.Не используйте регулярные выражения.Это почти наверняка излишне для этой задачи.Я немного разбираюсь в C ++, поэтому я не буду публиковать некрасивый код, но, по сути, вы могли бы проанализировать файл по одному символу за раз, поместив в буфер все, что не было #, затемзаписать его в выходной файл вместе с пробелом, когда вы нажмете #.В C # на ум приходит как минимум два очень простых способа решения:

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();

В качестве альтернативы вы можете заменить

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

на

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

I 'Я не говорю, что вы должны делать это одним из этих способов или моим предложенным методом для C ++, или что любой из этих методов идеален - я просто указываю здесь, что есть много способов проанализировать строки.Regex потрясающий и мощный и может даже спасти день в экстремальных обстоятельствах, но это не единственный способ разобрать текст, и может даже разрушить мир , если его использовать не для того.Действительно.

Если вы настаиваете на использовании регулярных выражений (или вынуждены делать это, например, при выполнении домашнего задания), тогда я предлагаю вам послушать Криса и использовать Boost.Regex.Кроме того, я понимаю, что Boost также имеет хорошую библиотеку строк, если вы хотите попробовать что-то еще.Просто обратите внимание на Ктулху , если вы используете регулярное выражение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...