2012-01-16 13 views
6

Tengo una función para leer el valor de una variable (entero, doble, o boolean) en una sola línea en una ifstream:Ignorar marcas de orden de bytes en C++, la lectura de una corriente

template <typename Type> 
void readFromFile (ifstream &in, Type &val) 
{ 
    string str; 
    getline (in, str); 
    stringstream ss(str); 
    ss >> val; 
} 

Sin embargo , falla en los archivos de texto creados con editores que insertan una lista de materiales (byte order mark) al comienzo de la primera línea, que desafortunadamente incluye el bloque {Note, Word}. ¿Cómo puedo modificar esta función para ignorar la marca de orden de bytes si está presente al comienzo de str?

+0

¿Quiere decir la UTF-8 BOM? Eso es muy arcano ... –

Respuesta

11

(Estoy asumiendo que estás en Windows, ya que el uso U + FEFF como firma en UTF-8-files es sobre todo una cosa de Windows y simplemente se debe evitar en otro lugar)

se podían abrir el archivo como un archivo UTF-8 y luego verifique si el primer carácter es U + FEFF. Puede hacer esto abriendo un fstream basado en caracteres normales y luego usar wbuffer_convert para tratarlo como una serie de unidades de código en otra codificación. VS2010 todavía no tiene un gran soporte para char32_t, por lo que lo siguiente usa UTF-16 en wchar_t.

std::fstream fs(filename); 
std::wbuffer_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t> wb(fs.rdbuf()); 
std::wistream is(&wb); 
// if you don't do this on the stack remember to destroy the objects in reverse order of creation. is, then wb, then fs. 
std::wistream::int_type ch = is.get(); 
const std::wistream::int_type ZERO_WIDTH_NO_BREAK_SPACE = 0xFEFF 
if(ZERO_WIDTH_NO_BREAK_SPACE != ch) 
    is.putback(ch); 

// now the stream can be passed around and used without worrying about the extra character in the stream. 

int i; 
readFromStream<int>(is,i); 

Recuerde que esto debe hacerse en la secuencia de archivo como un todo, no dentro ReadFromFile en su stringstream, porque ignorando U + FEFF sólo debe hacerse si es el primer personaje en todo el archivo, si es todas. No debería hacerse en ningún otro lado.

Por otro lado, si eres feliz usando una corriente basada en carbón y que quiere es ignorar U + FEFF si están presentes, James Kanze sugerencia parece bien, así que aquí es una implementación:

std::fstream fs(filename); 
char a,b,c; 
a = fs.get(); 
b = fs.get(); 
c = fs.get(); 
if(a!=(char)0xEF || b!=(char)0xBB || c!=(char)0xBF) { 
    fs.seekg(0); 
} else { 
    std::cerr << "Warning: file contains the so-called 'UTF-8 signature'\n" 
} 

Además, si desea utilizar wchar_t internamente, las facetas codecvt_utf8_utf16 y codecvt_utf8 tienen un modo que puede consumir 'listas de materiales' para usted. El único problema es que wchar_t es ampliamente reconocido como inútil en estos días * y por lo tanto, probablemente no deberías hacer esto.

std::wifstream fin(filename); 
fin.imbue(std::locale(fin.getloc(), new std::codecvt_utf8_utf16<wchar_t, 0x10FFFF, std::consume_header)); 

* wchar_t no sirve para nada, ya que se especifica a hacer una sola cosa; proporcionar un tipo de datos de tamaño fijo que pueda representar cualquier punto de código en el repertorio de caracteres de un lugar. No proporciona una representación común entre locales (es decir, el mismo valor wchar_t puede ser personajes diferentes en diferentes lugares por lo que no necesariamente se puede convertir a wchar_t, cambiar a otro local, y luego convertir de nuevo a char con el fin de hacer iconv - al igual que las conversiones de codificación.)

La representación de tamaño fijo en sí no tiene ningún valor por dos razones; primero, muchos puntos de código tienen significados semánticos y, por lo tanto, entender el texto significa que debe procesar múltiples puntos de código de todos modos.En segundo lugar, algunas plataformas como Windows usan UTF-16 como la codificación wchar_t, lo que significa que un solo wchar_t ni siquiera es necesariamente un valor de punto de código. (Si el uso de UTF-16 de esta manera es incluso compatible con el estándar es ambiguo. El estándar requiere que cada carácter admitido por una configuración regional sea representable como un único valor wchar_t; Si ninguna configuración regional admite ningún carácter fuera del BMP, entonces UTF-16 podría ser visto como conforme.)

4

Tienes que comenzar leyendo el primer byte o dos de la secuencia, y decidir si es parte de una lista de materiales o no. Es un poco molesto, ya que solo puede putback un solo byte, mientras que normalmente quiere leer cuatro. La solución más simple es abrir el archivo, leer los bytes iniciales , memorizar cuántos necesita omitir, luego volver al principio y omitirlos.

+2

La lista de materiales UTF8 es * tres * bytes de longitud. Supongo que el flujo es de tamaño byte, ya que es un 'char'-stream, por lo que realmente no puede ser UTF16 o UTF32. –

+0

@KerrekSB Puede leer UTF-16 y UTF-32 como transmisiones 'char', siempre que tenga la configuración regional adecuada. Por otro lado, no sé qué harían con una lista de materiales. (En mi humilde opinión, el BOM debería ser realmente la responsabilidad de la transmisión, o más bien de la faceta 'codecvt' que usa.) –

+0

Había olvidado las configuraciones regionales. ¿Tiene que escribir el suyo, o hay un UTF-16 en el estándar? –

Cuestiones relacionadas