2011-06-19 4 views
6

Estoy usando Visual C++ Express 2010 ... y yo soy muy nuevo en C++.C++ Volver a escribir un archivo, pero dejando fuera todo antes de una palabra

Quiero leer un archivo, luego eliminar todo antes de la palabra "< --START ->" y volver a escribir el archivo con el resto.

Aquí está el código que tengo para leer el archivo hasta el momento:

#include "stdafx.h" 
#include <iostream> 
#include <fstream> 

using namespace std; 

int main() { 
    ifstream myReadFile; 
    myReadFile.open("text.txt"); 
    char output[500]; 
    int found; 
    int line; 
    if (myReadFile.is_open()) { 
    line = 0; 
while (!myReadFile.eof()) { 
    myReadFile >> output; 
    if(line > 20) { 
     cout << output; 
    } 
    line++; 
} 
} 
myReadFile.close(); 
system("PAUSE"); 
return 0; 
} 

Muchas gracias de antemano.

+0

Es "' '<--START-->" garantiza que sea la única cosa en su línea? – zwol

+0

erm ... podría hacerlo así :) pero preferiría que no fuera – DugoutSoccer

+0

No es difícil hacerlo de ninguna manera, pero tiene más opciones si la palabra mágica es siempre la única en la línea. – zwol

Respuesta

0

No puede leer un archivo a continuación, retire todo antes de la palabra "< --start ->" y reescribir el archivo con el resto, excepto en la memoria como respondida por Benjamin. De lo contrario, necesitas un archivo intermedio. En todos los casos, debe manejar varias condiciones de error. Esto debería hacerlo:

#include <iostream> 
#include <fstream> 
#include <string> 
#include <cstdio> 

using namespace std; 

int main() 
{ 
    if (rename("Text.txt", "Old.txt") == 0) 
    { 
     try 
     { 
      ifstream in("Old.txt"); 
      ofstream out("Text.txt"); 
      string line; 
      while (getline(in, line)) 
      { 
       size_t pos = line.find("<--START-->"); 
       if (pos != string::npos) 
       { 
        string remain = line.substr(pos + 11); 
        if (remain.size()) 
         out << remain << endl; 
        break; 
       } 
      } 
      while (getline(in, line)) 
       out << line << endl; 
     } 
     catch (exception&) 
     { 
      remove("Text.txt"); 
      rename("Old.txt", "Text.txt"); 
      cerr << "Error processing file" << endl; 
      return 1; 
     } 
     remove("Old.txt"); 
     return 0; 
    } 
    cerr << "Error renaming file, access right?" << endl; 
    return 2; 
} 

.

7

En primer lugar, el bucle while está mal. De hecho, tal while loop es casi siempre incorrecto.

Usted debe escribir el bucle como:

while (myReadFile >> output) 
{ 
    if (line > 20) { 
     cout << output; 
    } 
    line++; 
} 

su bucle while(!myReadFile.eof()) es incorrecto, porque la bandera eof (o cualquier otro indicador de fallo) se establece después un intento de leer de la corriente de falla; eso significa que, si el intento de lectura falla, todavía está produciendo, porque todavía está dentro del ciclo, y el resto del código en el ciclo todavía se ejecuta cuando de hecho no debería.

En mi versión, sin embargo, si un intento de leer (es decir myReadFile >> output) falla, luego regresó std::istream&implícitamente convierte en false, y el bucle se cierra inmediatamente. Y si no falla, el flujo devuelto implícitamente se convierte en true.

Por cierto, me parece que desea leer línea por línea, en lugar de palabra por palabra. Si es así, entonces usted debe escribir esto como:

std::string sline; //this should be std::string 
while (std::getline(myReadFile, sline)) 
{ 
    if (line > 20) { 
     cout << sline; 
    } 
    line++; 
} 

De nuevo vuelve std::getlinestd::istream. Si la lectura fue exitosa, la secuencia devuelta implícitamente se convierte a true y el ciclo continuará, o si no tuvo éxito, entonces implícitamente se convertirá en y el ciclo saldrá.

+2

+1, pero debe explicar * why * 'while (! Fstream.eof())' siempre está mal. – zwol

+0

ah gracias, sí entiendo por qué está mal :) código en php, pero C++ es otro idioma para mí! – DugoutSoccer

+0

@Zack: lo explicó. :-) – Nawaz

2
std::string file_contents = LoadFileAsString("text.txt"); 
std::string::size_type offset = file_contents.find("<--START-->"); 
std::ofstream("text.txt") << file_contents.c_str() + offset; 

Con LoadFileAsString define así:

std::string LoadFileAsString(const std::string & fn) 
{ 
    std::ifstream fin(fn.c_str()); 

    if(!fin) 
    { 
     std::string what = "LoadFileAsString() : Failed to open file \""; 
     what += fn + '\"'; 
     throw std::runtime_error(what); 
    } 

    std::ostringstream oss; 
    oss << fin.rdbuf(); 

    return oss.str(); 
} 
+2

Esto es bueno siempre y cuando el archivo no sea enorme – zwol

+0

@Zack: solo falla para valores de gran tamaño que son bastante poco característicos de los archivos de texto. –

+0

He estado publicando archivos de seguimiento de paquetes de varios gigabytes mucho últimamente, por lo que mi idea de "típico" quizás esté un poco sesgada en este momento. (Sí, esos no son texto, pero es una cuestión de mentalidad.) – zwol

1

Aquí hay una versión que no tiene que leer todo el archivo en memoria. Nota: usa C stdio, no iostreams (solo porque sé C stdio mucho mejor); lee desde la entrada estándar, escribe en la salida estándar.

#include <stdio.h> 

int 
main(void) 
{ 
    int c; 
    enum { BOF, LT, D1, D2, S, T1, A, R, T2, D3, D4, GO } state = BOF; 
    while ((c = getchar()) != EOF) 
     switch (state) 
     { 
     case BOF: state = c == '<' ? LT : BOF; break; 
     case LT: state = c == '-' ? D1 : c == '<' ? LT : BOF; break; 
     case D1: state = c == '-' ? D2 : c == '<' ? LT : BOF; break; 
     case D2: state = c == 'S' ? S : c == '<' ? LT : BOF; break; 
     case S: state = c == 'T' ? T1 : c == '<' ? LT : BOF; break; 
     case T1: state = c == 'A' ? A : c == '<' ? LT : BOF; break; 
     case A: state = c == 'R' ? R : c == '<' ? LT : BOF; break; 
     case R: state = c == 'T' ? T2 : c == '<' ? LT : BOF; break; 
     case T2: state = c == '-' ? D3 : c == '<' ? LT : BOF; break; 
     case D3: state = c == '-' ? D4 : c == '<' ? LT : BOF; break; 
     case D4: state = c == '>' ? GO : c == '<' ? LT : BOF; break; 
     case GO: putchar(c); break; 
     } 
    return 0; 
} 
+0

+1, me gusta el concepto - requiere solo 1 byte de memoria de trabajo y hace una cantidad óptima de trabajo - pero tiene un error: cuando Si encuentra una discrepancia, debe regresar al estado que coincida con el siguiente prefijo más largo que posiblemente coincida con los caracteres leídos hasta el momento. En particular, eso significa que leer un '<' en cualquier estado además de 'BOF' debería llevarte de regreso a' LT' en lugar de 'BOF'. (Para otras cadenas objetivo, las transiciones podrían ser más complicadas, específicamente, esto ocurre cuando aparece un prefijo apropiado de la cadena en otra parte de la cadena. Sugiero buscar en Google KMP string). –

+0

Realmente porque la cadena objetivo aquí es "simple" en el Tenga en cuenta que cada desajuste lo lleva al estado 1 o 2, su máquina de estados es básicamente "lineal", y puede simplificarlo hasta 'char needle [] =" <--START--> "; int estado = 0; while ((c = getchar())! = EOF) {if (aguja [estado]) {si (c == aguja [estado]) estado ++; else estado = c == aguja [0]; } else putchar (c); } ' –

+0

Tienes razón. Me voy de mi máquina de estado deletreada (con el error corregido) porque creo que es más educativo de esa manera, pero me gusta su simplificación "lineal". – zwol

0

Bueno, ya hay muchas respuestas aquí, pero aquí hay otra. Es muy corto porque es súper simple, es de una sola pasada, perfectamente portátil, y no asigna nada en el montón.

#include <iterator> 
#include <istream> 
#include <ostream> 

void strip_file_beginning(std::istream &in_s, std::ostream &out_s) { 
    std::istreambuf_iterator<char> in(in_s), in_end; 
    std::ostreambuf_iterator<char> out(out_s); 
    static char const start_word[] = "<--START-->"; // mind the terminating '\0' 

    for (char const *pen = start_word; pen != start_word + sizeof start_word - 1 
            && in != in_end; ++ in) { 
     if (* in == * pen) ++ pen; 
     else pen = start_word; 
    } 

    std::copy(in, in_end, out); 
} 

http://ideone.com/zh9bd

Cuestiones relacionadas