[ Foro de Pascal ]

Cómo leer un archivo como un listado (arrays).

07-Dec-2011 23:53
Angel Bravo
14 Respuestas

Pues esa es la cuestión...tengo un programa que utiliza un array para guardar los números con los cuales realizar operaciones estadísticas (media, mediana, varianza,etc..) y quería añadir la opción de que leyera los datos no sólo introduciéndolos a mano, sino por medio de un archivo donde ya estuvieran escritos esos datos. El programa es bastante largo pero básicamente la idea es, teniendo un array:

Type
listado = array [1..N] of real;

utilizas

Var
       numeros:listado;

donde metes los datos pero,¿Cómo tiene que ser el archivo (creo que no puede ser de texto)?¿binario?¿Cómo deben ir introducidos los datos?¿cómo hago para leerlo?. He mirado en cien sitios pero no atino en cómo hacerlo, agradecería un poco de orientación, gracias.


08-Dec-2011 00:14
Angel Bravo

Con idea de que mi pregunta se entienda mejor, pongo una versión reducida del programa, éste calcula únicamente la mediana (funciona perfectamente en free pascal salvo, obviamente el cuerpo del programa destinado a leer los datos desde un archivo que está en modo comentario).

Program estadistica (archivo, arch);
(* Programa que recoge un listado de hasta 100 números,
*  los escribe en pantalla y opera con ellos *)
Uses
crt;

const

N=100;

Type
listado = array [1..N] of real;

Var
cantidad:byte;
numeros:listado;
resultado:real;
opcion:char;
//archivo:file of listado;

Procedure cantidadnumeros;
Begin
writeln('¿Cuantos números son?');
readln(cantidad);
ClrScr;
End; (*menu*)

procedure leernumeros (var num:listado);
Var
lista: 1..N;
Begin            
For lista:=1 to cantidad do
       begin
repeat (* bucle que evita el runtime error 106 *)
Write(' Introduzca el nº ' ,lista, ': ');
{$I-}
Readln(num[lista]);
{$I+}
until ioresult=0;
       end;
   End;  

Procedure Escribirnumeros (num:listado);
Var
lista: 1..N;
Begin
   write('(');      
For lista:=1 to cantidad do
Write(num[lista]:5:2);
write(' )');
End;

Procedure ordenar (cantidad: byte; var orden:listado);
Var
i, j: integer;
inicio:real;

Begin
For i := 1 to cantidad do
Begin
inicio := orden[i];
j := i-1;
While ((j >= 0) AND (orden[j] > inicio)) do
Begin
orden[j+1] := orden[j];
j := j - 1;
End;
orden[j+1] := inicio;
End;
End;

procedure mediana (cantidad: byte; orden:listado; var resultado:real);

Begin

if (cantidad mod 2) = 1 then
begin
resultado:=orden[(cantidad div 2)+1];
write('la mediana es', resultado:5:2);
end
else
begin
resultado:=(orden[(cantidad div 2)] + orden[(cantidad div 2)+1])/2;
write('la mediana es', resultado:5:2)
end;
end;

Begin
writeln('¿(i)ntroducir datos o leer desde un (f)ichero?. ');
readln(opcion);
case opcion of
'i' : begin
cantidadnumeros;
leernumeros(numeros);
writeln;
writeln(' listado de números: ');
escribirnumeros(numeros);
writeln;
ordenar (cantidad, numeros);
escribirnumeros(numeros);
writeln;
mediana (cantidad,numeros,resultado);
writeln;
end;
'f' : begin
(*assign(archivo,'archivo');
reset(archivo);
if not eof( archivo ) then
//readln( archivo, listado[i]);
writeln('hecho');
Close(archivo);
//rewrite(arch);
//read (archivo);
writeln(' listado de números: ');
writeln (archivo);
writeln;
ordenar (cantidad, numeros);
escribirnumeros(numeros);
writeln;
mediana (cantidad,numeros,resultado);
writeln;*)
end;
end;

end.

Céntrandonos en el case 'f', ¿cómo hago para que me lea el archivo?. partimos de la base que el archivo se encuentra en la misma carpeta que el programa.


08-Dec-2011 03:28
Antonio P.G.

Hola, Ángel, qué tal. Sé que muchas cosas que menciono las conocerás seguro, pero teniendo en cuenta que es un foro, espero que me permitas extenderme un poco.


La idea la llevas más o menos, pero falta alguna cosilla. Antes que nada, decirte que al igual que has creado procedimientos para los cálculos de los estadísticos, podrías crear unos procedimientos de lectura de datos, de tal manera de que cuando se elija la opción "i", se realice el procedimiento "LeerDatosTeclado (....)" y de forma análoga para la opción "f".

Vamos con lo del archivo. Pueden utilizarse archivos de texto y archivos de registros (".dat"). Mi recomendación son los de texto. ¿Por qué? Pues porque a la hora de manipular los casos o datos desde afuera (con un bloc de notas, por ejemplo), es mucho más sencillo. Así es más fácil buscarle las cosquillas al programa después (con esto me refiero a la prueba de casos extremos, erróneos...).

Dejemos claro que un archivo de los que estamos pensando tiene esta pinta, más o menos:

                                     (Seguiría hacia la derecha)
----------------------------------------------------->
|8.9
|-4.5
|-0.2639
|
|
(Seguiría hacia abajo)

NOTA: No he escrito casos de números elevados a la potencia (E) porque en este momento no recuerdo si la "E" va delante o detrás...

En un principio tú no vas a saber cuantos datos hay (esperemos que no sean más que la constante "N" porque si no la habremos piciado, en el sentido de que se producirá un desbordamiento en el array de datos). Es decir, pensemos que vamos a recibir un número indeterminado de datos menor que "N", pero que no sabemos cuantos son. ¿Qué hacemos? Leer hasta que se acabe el archivo. ¡Ajá! Esto suena a bucle pero... ¿qué tipo usamos? Recordamos un poco bucles:

1.
**************************************
  for i:= a to b do begin
  end; {sin begin  ni end si es solo una instrucción).

El bucle "for" se utiliza cuando sabemos con exactitud cual es el número de veces que se va a dar la vuelta. Éste no es nuestro caso.
**************************************

2.
**************************************
  repeat
  until condición;

El bucle "repeat...until" se utiliza cuando al menos se va a ejecutar una vuelta. ¿Realmente sabemos que en nuestro archivo hay datos? No, así que, ¿por qué íbamos a ejecutarlo una vez si ni siquiera hay datos?
**************************************

3.
**************************************
   while condicion do begin
   end;

El bucle while chequea una condición antes de entrar. Si ésta se cumple, se ejectua y vuelve a chequear. Tan pronto como la condición no se cumpla, no se ejecutará el bucle y se pasará a la siguiente instrucción. Éste es el que queremos.
*************************************

NOTA: esta es la forma "clásica" de utilizar los bucles. No he tenido en cuenta las instrucciones "break" y "continue", que a mi parecer, no tienen tanto que ver con la programación estructurada.

Pues bien, como decía, el bucle es while. La frase será algo así como "mientras no hayamos llegado al final del archivo, leeré el número de la línea":

************************************
{Se suponen variables y demás}
assign (archivo,ruta);
reset (archivo);
while not eof(archivo) do
 readln (archivo, numero);
***********************************

¿Que lo tienes en un vector? No hay problema:

************************************
{Se suponen variables y demás}
assign (archivo,ruta);
reset (archivo);
i:=1;
while not eof(archivo) do begin
 readln (archivo, numero[i]);
 Inc (i);   {Aquí podíamos haber puesto "i:=i+1" también. }
end;
***********************************
Vamos, que es como leer desde el teclado.

Consejo: no olvides nunca cerrar los archivos ("close(archivo)"). La instrucción "rewrite(archivo)" blanquea la hoja y la empieza desde el principio, pero "append(archivo)" añade al archivo desde abajo.

Aprovecho a hacer alguna anotación sobre tu código. Lo cierto es que está muy bien. Si no lo tienes sangrado (tal vez el editor de aquí no te lo permita), procura hacerlo, puesto que clarifica un montón. Además, unos comentarios ayudarían (bueno, si el código se vuelve un poco más complicado).

Por cierto, ¿por qué no pruebas a dejar el máximo "N=100", pero que se puedan leer menos de "N"? Tan solo habría que definir una variable "cantidad_numeros", por ejemplo, y que los procedimientos se ejecutasen en base a esto y no "N"... (Lo digo porque sería más cómodo para probar casos).

Un par de preguntillas:
-¿Estás pensando en alargar el programa, de incluir más estadísticos?
-¿Sabes por qué te da un error 106?
-¿Por qué en la primera línea escribes "Program estadistica (archivo, arch);"?¿Son parámetros del programa?

Espero haberte servido de ayuda.

Ciao.


08-Dec-2011 10:47
Angel Bravo

Gracias por tu respuesta, veamos..tu primera porción de código:

************************************
{Se suponen variables y demás}
assign (archivo,ruta);
reset (archivo);
while not eof(archivo) do
 readln (archivo, numero);
***********************************

Presupongo que la idea es que cada dato del archivo sea seleccionado como dato individual, que es lo que se necesitaría, no? al escribirlo en el programa quedaría así, pero surgen dos cuestiones, primero, pongo el código del programa:

Program estadistica;
(* Programa que recoge un listado de hasta 100 números,
*  los escribe en pantalla y opera con ellos *)
Uses
crt;

const

N=100;

Type
listado = array [1..N] of real;

Var
cantidad:byte;
numeros:listado;
resultado:real;
opcion:char;
archivo:file of listado;

Procedure cantidadnumeros;
Begin
writeln('¿Cuantos números son?');
readln(cantidad);
ClrScr;
End; (*menu*)

procedure leernumeros (var num:listado);
Var
lista: 1..N;
Begin            
For lista:=1 to cantidad do
       begin
repeat (* bucle que evita el runtime error 106 *)
Write(' Introduzca el nº ' ,lista, ': ');
{$I-}
Readln(num[lista]);
{$I+}
until ioresult=0;
       end;
   End;  

Procedure Escribirnumeros (num:listado);
Var
lista: 1..N;
Begin
   write('(');      
For lista:=1 to cantidad do
Write(num[lista]:5:2);
write(' )');
End;

Procedure ordenar (cantidad: byte; var orden:listado);
Var
i, j: integer;
inicio:real;

Begin
For i := 1 to cantidad do
Begin
inicio := orden[i];
j := i-1;
While ((j >= 0) AND (orden[j] > inicio)) do
Begin
orden[j+1] := orden[j];
j := j - 1;
End;
orden[j+1] := inicio;
End;
End;

procedure mediana (cantidad: byte; orden:listado; var resultado:real);

Begin

if (cantidad mod 2) = 1 then
begin
resultado:=orden[(cantidad div 2)+1];
write('la mediana es', resultado:5:2);
end
else
begin
resultado:=(orden[(cantidad div 2)] + orden[(cantidad div 2)+1])/2;
write('la mediana es', resultado:5:2)
end;
end;

Begin
writeln('¿(i)ntroducir datos o leer desde un (f)ichero?. ');
readln(opcion);
case opcion of
'i' : begin
cantidadnumeros;
leernumeros(numeros);
writeln;
writeln(' listado de números: ');
escribirnumeros(numeros);
writeln;
ordenar (cantidad, numeros);
escribirnumeros(numeros);
writeln;
mediana (cantidad,numeros,resultado);
writeln;
end;
'f' : begin
assign(archivo,'archivo');
reset(archivo);
while not eof (archivo) do
readln( archivo, numero); ¿? <-----
writeln('hecho');
Close(archivo);
writeln(' listado de números: ');
//writeln (archivo); -> ¿Para que escriba en pantalla
// los datos del archivo?.
writeln;
ordenar (cantidad, numeros);
escribirnumeros(numeros);
writeln;
mediana (cantidad,numeros,resultado);
writeln;
end;
end;

end.

en él indico las dudas...en la línea "readln( archivo, numero)" "numero" sería el listado que yo he llamado "numeros", ¿no? y segundo, cómo hago para que muestre el listado de números del archivo igual que cuando se introducen por el teclado

Obviamente el código está tabulado si no, sería una locura como tu bien dices (no sé por qué siempre que pongo código aquí desaparece el sangrado) la falta de comentarios viene dada por lo que decia antes..este no es EL programa, sino una version reducida del programa que hice para que se entienda mi pregunta.

Cuando te refieres a que haga una variable "cantidad_numeros"...no te sigo muy bien ¿no es lo mismo que el procedimiento al que he llamado "cantidadnumeros"?

Por lo que te decía antes, el programa es más largo, ya tiene media, mediana (que la cogí para este programa de muestra), varianza, coeficientes de correlación...y espero ir haciéndolo cada vez más grande hasta abarcarlo todo lo posible...o que me harte, lo que primero ocurra :P Aún sigo trasteando el de matrices que puse aquí hace años...

En lo relativo al error 106...pues si mal no recuerdo era cuando hacías que el programa asignase un valor equivocado a una variable (en este caso, que te equivocases y pusieras sin querer una "j" como si fuera un número del listado...odiaba que el programa se saliese y me escupiera error por algo así...imagina que llevas 50 números y te equivocas al escribirlo, el ordenador saldría por los aires)

¿Que por qué escribo en la primera línea  "Program estadistica (archivo, arch);"? Pues porque no tenía ni idea de como había que hacerlo xD en cada sitio que buscaba información te decían una cosa distinta...Gracias por todo y a ver si lo saco de una vez..


08-Dec-2011 11:47
Antonio P.G.

Hola y discúlpame pero creo que he sido un poco desastre explicándome. A ver, por partes:

Mira este trozo de código:
*********************************
program;
   var
     numero : integer; { Una variable para un entero. }
begin
 assign (archivo, ruta);  { Recordamos que la ruta es de tipo string. El archivo contiene datos especificados de la misma forma que arriba. }
 reset (archivo);
 while not eof(archivo) do begin
    readln (archivo, numero);
    writeln (numero);  { Imprimimos el numero que se acaba de leer en la pantalla. }
 end;
 close (archivo);
end.
**********************************

En este programa vas leyendo los números del archivo, y según los lees, se escriben. ¿Cuántos de ellos almacenas? Pues solamente el último. Ahora bien, mira este otro programa:
*********************************
program;
   const
      MAX = 100;   { Máximo de números que leeremos. }
   var
     lista_numeros : array [1..MAX] of integer; { Una array para enteros. }
     contador : integer;
     tenemos  : 1..MAX; { Número de datos introducidos por el usuario a través de un archivo o del teclado. }
begin
 assign (archivo, ruta);  { Recordamos que la ruta es de tipo string. El archivo contiene datos especificados de la misma forma que arriba. }
 reset (archivo);
 contador:= 0;
 while not (eof(archivo) or i&gt;MAX)  do begin
    Inc (contador);
    readln (archivo, lista_numeros[i]);
    writeln (lista_numeros[i]);  { Imprimimos el numero que se acaba de leer en la pantalla. }
 end;
 if i&gt;MAX then
   tenemos:= MAX  { Aquí podríamos poner también "tenemos:= i-1". }
 else
   tenemos:= i;
 close (archivo);
end.
**********************************

Con este programa habrías guardado varios números, como mucho 100 (por la variable "MAX"). En la condición en incluido el "MAX", porque si se llegase a "lista_numeros[101]" se estaría pidiendo algo que no existe, puesto que MAX es 100. Así, la frase de la condición quedaría como " mientras que no hayamos llegado al final del archivo o mientras que no sea "i" mayor que "MAX", haz el bucle".

Por otro lado, ¿ves el uso que he hecho de la variable "tenemos"? De esta forma le estoy dando al usuario el poder introducir cualquier número entre 0 y MAX, y no siempre MAX. Lo único que tienes que hacer en tu programa (en el completo) es, en vez de referirte a MAX en los procedimientos, referirte a "tenemos".

NOTA: Lo he denominado "tenemos" pero lo habría denominado "cantidad_numeros". Lo que ocurre es que tú tienes, al parecer, un procedimiento denominado de esa misma forma.

Si quieres alguna de las siguientes cosas:
- que te cuente alternativas a lo del error 106 (no lo había visto jamás).
- que te dé una idea un poco más práctica del programa, para que puedas probar bien y verificar después que funciona, echando menos horas.
- modular un poquillo el programa.

Coméntamelo, y vamos por pasos, que si no te atiborro de cosas y te estalla la cabeza (y aparte que ahora no tengo tiempo). Pero antes, que haya quedado claro lo que he puesto en este post.

¡Ah! Y fíjate que se puede poner "program;" y ya. Lo que tú estados haciendo, era pasar parámetros al programa (y creo yo que no quieres eso...)

Ciao.


08-Dec-2011 16:20
Angel Bravo

Me pierdo un poco en el uso que le das al "MAX" ¿Qué diferencia hay entre tu MAX y mi N? pongo tus variables con las mías en paréntesis, básicamente parece que es el mismo código:

const
       (N)MAX = 100;  { Máximo de números que leeremos. }
var
       (listado)lista_numeros : array [1..(N)MAX] of (real)integer; { Una array para enteros. }
       contador : integer;
      (numeros)tenemos  : 1..(N)MAX;
      ¿¿numeros:listado = numeros:1..(N)MAX;??   -> ¿No es lo mismo?

Si el único uso de la variable MAX sería simplemente no pasarse con los números más allá de la constante
casi mejor poner algo así, y que juzgue antes de que lea (bien desde un archivo o a mano) no?:


Procedure cantidadnumeros;
   Var
       correcto: Boolean;

   Procedure Compruebatamanyo;
       Begin
           if cantidad > N then
           correcto := False;
           End; (*compruebatamanyo*)

     Begin
        Repeat
            repeat(* bucle que evita el runtime error 106 *)
            correcto:=true;
            writeln('¿Cuantos números son?.');
            {$I-}
            readln(cantidad);
            {$I+}
            until ioresult=0;
            if cantidad > N then
            begin
                writeln('El número máximo son 100 números vuelve a intentarlo');
                correcto:=false
            end;
            Compruebatamanyo;
        Until
        correcto;

De cualquier manera el "pasarme" o no de números me da un poco "igual" en el sentido de que lo que me preocupa no es eso, sino leer archivos..lo unico que quiero es tener funcionando la sentencia CASE del programa que puse. Ya a partir de ahi...
En la orden "assign (archivo, ruta);" "archivo" es el nombre del archivo, ¿no? ¿pero qué es la ruta?

Por cierto, ¿cómo deben ir los datos en el archivo? ¿deben ir separados por espacios o con un retorno del carro?


08-Dec-2011 17:05
Antonio P.G.

Hola.

Sí, "N" y "MAX" cumplen la misma función.

Un ejemplo de instrucción "assign":

*********************************************
program;
   var
     unSoloNumero : real;
     elArchivo    : text;
     miRuta       : string;
begin
 assign (elArchivo, 'C:\Documents and Settings\numeros.txt');
 {
 Otra opción habría sido:
   miRuta:= 'C:\Documents and Settings\numeros.txt';
   assign (elArchivo, miRuta);
 }
 reset (elArchivo);
 readln (elArchivo, unSoloNumero);
 close (elArchivo);
 writeln ('Este es el primer y único número leído del archivo: ', unSoloNumero);
 writeln ('Presione ENTER para salir.');
 readln;  { El usuario presionará ENTER. }
end.
*********************************************

Espero que se vea claro como funciona assign.

Con respecto a cómo deberían ir los datos en el archivo, hay varias formas. Una es la que dije yo antes. El procedimiento "while" está adaptado a esa estructura (normalmente se utilizaría un diferente código para cada tipo de estructura).

Tendríamos diferentes formas entonces, con dos divergencias principales que se combinan:

1. Los datos están en diferentes líneas (cada uno debajo del anterior) o están en una misma línea.
2. Al comienzo se indica o no la cantidad de datos que se va a leer.

NOTA: El caso que hemos tratado es en el que cada número esta debajo del anterior y no se conoce el número total de datos.

¿Cómo planteamos el código según el tipo de diseño?Veamos:
1. Cada número debajo del anterior --> Sólo usaremos "readln's" en el bucle.
  Varios números en una sola línea --> Utilizaremos "read's" en el bucle y al final haremos un salto de carro "readln".

2. No se conoce el número total de elementos --> While.
  Sí se conoce --> Se lee el número total de elementos en la primera línea y después utilizamos un bucle de tipo "for".

Espero haber saciado tu curiosidad.


08-Dec-2011 18:39
Angel Bravo

el programa de tu último mensaje compila con un warning "Local variable "mi ruta" not used".

y al ejecutarse me escupe:

Runtime error 2 at $000000000040023E
 $000000000040023E
 $00000000004001D8

:-/


08-Dec-2011 21:16
Fulanito de Tal

Hola. Voy a intentar ayudar a Angel Bravo en este tema, aunque a lo mejor meto la pata. Si es así pido disculpas.

Es conveniente guardar los datos en un fichero de texto porque, como bien dijo Antonio, se pueden escribir y modificar "a mano", abriéndolos con un simple editor de textos. También se pueden leer y escribir con programas escritos en diferentes lenguajes. Si fuese un archivo binario sólo se podría leer con el mismo lenguaje con el cual fue escrito, ya que cada lenguaje de programación escribe los ficheros binarios de forma diferente. Además podremos escribir o modificar el fichero casi con total libertad. En un fichero binario si por ejemplo hemos declarado uno de los campos como variable de tipo string de longitud 20, y luego queremos modificar el código para que admita cadenas más largas corremos el riesgo de perder todo lo que tenemos escrito en fichero, es decir, que se borre completamente. A mí me ha pasado personalmente. En una agenda telefónica había declarado la variable "nombre" así: nombre: string [20]. Pero al pasar unos días el nombre de un establecimiento comercial tenía más de 20 caracteres. Entonces me fui al código del programa, cambié la variable como string[50], lo compilé, y... ¡se me borró todo el fichero con todos los datos telefónicos, más de 100! Todo el trabajo tirado por la borda. En un fichero de texto no habría tenido este problema, pues el nombre podría ser tan largo como quisiera (siempre que no rebase la capacidad de una variable string).
Perdón por el rollo que he soltado, tengo tendencia a hablar de más... Con todo esto quiero decir que deberíamos usar ficheros de texto siempre que ello sea posible. El problema que tienen estos ficheros es que los datos son caracteres, si queremos hacer operaciones matemáticas con ellos, tenemos que transformarlos en números una vez leídos. Para eso usaremos la instrucción "StrToFloat" que nos convierte una cadena de caracteres en un número real. Pero la función StrToFloat no está en free pascal standard. Para poder usarla hay que utilizar la unidad SysUtils, mediante la clausula "uses". Nuestro programa deberá empezar así:
uses
sysutils;

Copio aquí un pequeño programa que lee números reales de un fichero tipo texto, los almacena en un vector, y calcula el número de datos, la suma y la media. Espero que con ello te hagas una idea más o menos.

Antes de ejecutar el programa, crearemos con un editor de textos un fichero donde escribiremos los datos. Como máximo tendrá 100 datos. Cada dato va en una línea. Esto es muy importante porque si no existe el fichero, el programa dará error. Hay una forma de evitar esto, pero es otro asunto. Este fichero ha de guardarse en el mismo directorio donde está el programa compilado, con el nombre "datos". Si queremos otro nombre y otro directorio debemos especificarlo en la línea assign (fichero_datos, 'nuevonombre_o_nuevodirectorio'');
Por si hay dudas: el fichero que está escrito en disco se llama 'datos', pero para que el programa pueda trabajar con él, hay que asignarlo a una variable, que hemos llamado "fichero_datos". Es igual que cuando se asigna un número a una variable, por ejemplo a:= 17. Ahora el programa puede hacer operaciones matemáticas con "a". En el caso de los ficheros se usa la sentencia assign (nombre_de_la_variable, 'fichero''). El programa trabajará siempre con "nombre_de_la_variable", no con "fichero".

uses
sysutils;

var
fichero_datos: text;
numero_datos, bucle: byte;
suma, media: real;
dato_original: string;
vector_datos: array [1..100] of real;

begin
assign (fichero_datos, 'datos'');
reset (fichero_datos);

numero_datos:= 1;

while not eof (fichero_datos) do
begin
readln (fichero_datos, dato_original);
vector_datos [numero_datos]:= strtofloat(dato_original);
numero_datos:= numero_datos + 1;
end;

close (fichero_datos);

numero_datos:= numero_datos - 1;

suma:= 0;

for bucle:=1 to numero_datos do
begin
suma:= suma + vector_datos [bucle];
end;

media:= suma / numero_datos;

writeln ('Resultados');
writeln ('----------');
writeln ('Numero de datos: ', numero_datos);
writeln ('Suma: ', suma:0:10);
writeln ('Media: ', media:0:10);

end.

----------------

Comentamos un poco el programa.
Al principio,se hace un llamamiento a la unidad "SysUtils" para poder hacer conversión de cadena de caracteres a número real.
Se declara una variable fichero_datos como "text", eso significa que será un fichero de texto.
La variable "dato original" almacena el dato leído del fichero pero tal como está: string, cadena de caracteres. Es una variable que almacena provisionalmente el dato.
Como máximo escribiremos en el fichero 100 datos, cada uno en una línea. Ejemplo:

225
-13.58
85.63

Finalmente hay una línea que puede desconcertar algo: numero_datos:= numero_datos - 1; Esta línea se debe a que se lee una línea de más, una línea en blanco al final del fichero. (El motivo no lo sé muy bien, aunque sospecho que es por usar la sentencia "readln", que siempre salta a la linea siguiente después de haber leído la actual). Para calcular bien la media hay que restar esa línea de más que se ha leído.

Una última cosa, espero que no te parezca mal porque es un consejo. Deberías leerte los capítulos 11.1 y 11.2 del curso de pascal de esta misma web.

http://www.aprendeaprogramar.com/course/view.php?id=7

En esos capítulos explican como leer y escribir en ficheros de texto. Es para aprender lo elemental a la  hora de trabajar con esos ficheros.


08-Dec-2011 21:49
Fulanito de Tal

Corrijo un error mio. El motivo de incluir la línea numero_datos:= numero_datos - 1 es por esta secuencia del código:

while not eof (fichero_datos) do
begin
readln (fichero_datos, dato_original);
vector_datos [numero_datos]:= strtofloat(dato_original);
numero_datos:= numero_datos + 1;
end;

Supongamos que el fichero tiene 8 datos. Se lee el dato número 8 en la linea readln (fichero_datos, dato_original). Pero dos líneas más abajo la variable numero_datos se incrementa en 1. Por eso el valor de esta variable es 9, no 8. Yo había supuesto que esto se debía a haber usado la sentencia "readln", pero no es así.


08-Dec-2011 23:38
Antonio P.G.

Buenos posts, Fulanito de tal. (Vaya fastidio lo de la agenda!).

Primero me dirijo a Ángel:

Efectivamente, Ángel, el tema de archivos viene en el curso, y yo también te recomiendo leerlo, no solo por aprende, sino porque están súper bien explicados por el profesor Nacho.

Tu programa. El error en tiempo de ejecución que has tenido indica que no se puede encontrar el archivo. Asegúrate de que el archivo existe o de que la ruta (ejemplo: 'C:\pascal\abc.txt'). Por otro lado, si la variable "ruta" no la utilizas, elimínala del mapa.

Fulanito de tal, con respecto a lo de que no se pueden leer números de archivos de texto, si que es posible: copia este código que me invento aquí compílalo, y ejecútalo:
**************************************
program;

 var
  i : 1..5;
  numero : array [1..5] of integer;
begin
 assign (archivo,'datos.txt');
 reset (archivo);
 for i:= 1 to 5 do begin
   readln (archivo,numero[i]);
   writeln (numero[i]);
 end;
 close (archivo);
end.
**************************************
(Además, créate un archivo de texto, con cinco enteros, uno encima del otro y llámalo "datos.txt")

De todas maneras muchas gracias por la función "strtofloat", es probable que la utilice, puesto que la "magia" de esta función es que te permite pasar un string, o una parte de éste, a número real. Así podemos leer siempre un string, comprobar que es un número y en caso de fallo (en vez de romperse la ejecución) podemos reportarlo.

Un saludo.


09-Dec-2011 01:10
Fulanito de Tal

Anda, pues no sabía que se podía leer directamente como número desde un fichero de texto. Gracias.

Con respecto a las funciones de conversión, las que yo conozco son:

StrToFloat
FloatToStr
StrToInt
IntToStr

Las dos últimas son para convertir enteros a cadenas y viceversa.
También puedes usar la letra 'e' para indicar el exponente de una potencia de diez. Por ejemplo la variable tipo string c:= '2e77' se puede convertir perfectamente a real mediante strtofloat.
Un saludo.


09-Dec-2011 20:26
Angel Bravo

Ante todo, gracias por la dedicación y los extensos post orientándome :_-) Bueno, vamos a ver si soy capaz de explicarlo todo....como decía Jack el
destripador, vamos por partes:

Los capitulos que mencionáis me los leí (leí bastante antes de preguntar) pero no solucionaban el error que me estaba volviendo loco y que ahora he visto...
cogí el último código que Antonio ha puesto y lo adapte dando lugar a este programa

Program prueba;

Const
   N=100;

Var
   cantidad,i : byte;
   listado : array [1..N] of real;
   archivo:text;

Procedure cantidadnumeros;
   Begin
       write('¿Cuantos números? ');
       readln(cantidad);
   End;


begin
   cantidadnumeros;
   assign (archivo,'datos.txt');
   reset (archivo);
   for i:= 1 to cantidad do
   begin
       readln (archivo,listado[i]);
       write (listado[i]:5:0);
   end;
   close (archivo);
   writeln;
end.


Es un programa muy simple, muestra en pantalla la cantidad de números que quieras del archivo seleccionado. Pues bien ¿Dónde estaba el error? en la declaración de variables... observad que en el ejemplo se declara como:

 Var
     listado : array [1..N] of real;

sin embargo, lo que yo hacía para operar con el array era ésto:

 Type
     listado = array [1..N] of real;

 Var
     numeros:listado;

Y utilizaba "numeros" para llamadas a procedimientos (por ejemplo ==> Function varianza (numeros:listado):real;) etc.. al hacerlo así cuando en el ejemplo llegamos a la línea:

 readln (archivo,listado[i]);

El compilador vomita:

 Error: Variable identifier expected

Que era ESE el error que se me escapaba ¿Cómo llamar a los procedimientos? no puedes declarar a la vez como Var Numeros:listado y listado : array [1..N] of real;


11-Dec-2011 01:57
Angel Bravo

Ya he resuelto mis dudas, muchísimas gracias a los dos. Aquí pongo el código del programa que puse antes, terminado (o al menos, que funciona :-P ) por si a alguien le interesa (de un listado de números, los muestra ordenados y calcula la mediana, pudiendo seleccionar si los datos se introducen desde un fichero llamado "datos.txt" o manualmente desde el teclado).


Program prueba;

Uses
       crt;

const
       N=100;

Type
       listado = array [1..N] of real;

Var
       cantidad, i:byte;
       numeros:listado;
       resultado:real;
       opcion:char;
       archivo:text;

Procedure cantidadnumeros;
       Begin
               writeln('¿Cuantos números son?');
               readln(cantidad);
               ClrScr;
       End;    (*menu*)

procedure leernumeros (var num:listado);
       Var
               lista: 1..N;
       Begin            
               For lista:=1 to cantidad do
               begin
                       repeat (* bucle que evita el runtime error 106 *)
                               Write(' Introduzca el nº ' ,lista, ': ');
                               {$I-}
                               Readln(num[lista]);
                               {$I+}
                       until ioresult=0;
               end;
       End;

Procedure Escribirnumeros (num:listado);
        Var
               lista: 1..N;
        Begin
               write('(');      
               For lista:=1 to cantidad do
               Write(num[lista]:5:2);
               write(' )');
       End;

Procedure ordenar (cantidad: byte; var orden:listado);
       Var
               i, j: integer;
               inicio:real;

       Begin
               For i := 1 to cantidad do
               Begin
                       inicio := orden[i];
                       j := i-1;
                       While ((j >= 0) AND (orden[j] > inicio)) do
                       Begin
                               orden[j+1] := orden[j];
                               j := j - 1;
                       End;
                       orden[j+1] := inicio;
               End;
       End;

procedure mediana (cantidad: byte; orden:listado; var resultado:real);

       Begin

               if (cantidad mod 2) = 1 then
                       begin
                               resultado:=orden[(cantidad div 2)+1];
                               write('la mediana es', resultado:5:2);
                       end
               else
                       begin
                               resultado:=(orden[(cantidad div 2)] + orden[(cantidad div 2)+1])/2;
                               write('la mediana es', resultado:5:2)
                       end;
end;

Begin
   writeln('¿(i)ntroducir datos o leer desde un (f)ichero?. ');
   readln(opcion);
   case opcion of
               'i' : begin
                       cantidadnumeros;
                       leernumeros(numeros);
                       writeln;
                       writeln(' listado de números: ');
                       escribirnumeros(numeros);
                       writeln;
                       ordenar (cantidad, numeros);
                       escribirnumeros(numeros);
                       writeln;
                       mediana (cantidad,numeros,resultado);
                       writeln;
               end;
               'f' : begin
                       assign (archivo,'datos.txt');
                       reset (archivo);
                       i:=0;
                       cantidad:=0;
                       ClrScr;
                       write('(');
                       while not eof(archivo) do
                       begin
                       readln (archivo, numeros[i]);
                               write (numeros[i]:5:0);
                               i:=i+1;
                               cantidad:=cantidad+1;
                       end;
                       write(' )');      
                       writeln;
                       ordenar (cantidad, numeros);
                       escribirnumeros(numeros);
                       writeln;
                       writeln;
                       mediana (cantidad,numeros,resultado);
                       writeln;
                       close (archivo);
                       writeln;
               end;
   end;
end.


15-Dec-2011 15:23
Nacho Cabanes (+84)

Angel, el programa va teniendo muy buena pinta. Aun así, un par de comentarios, más referidos a estilística que a funcionamiento:

- Yo usaría "ioresult" para comprobar si el fichero existe, pero no para leer los datos hasta el final. Para eso emplearía "while not eof".

- Sé consistente con el uso de parámetros: en unos casos estás pasando el array y su tamaño, pero en otros sólo pasas el array y "te fías" de una variable global, lo que es bastante peligroso.

- Las funciones que devuelven un valor, suele ser preferible declararlas como "function", y dejar los "procedure" con parámetros por referencia para dos casos muy concretos:

* Cuando hay que devolver más de un valor de una función (cosa que no se puede hacer en una función normal, salvo que devuelvas un array).

* Cuando se trata de parámetros que ocupan mucho espacio y quieres optimizar la velocidad del programa, evitando que se haga una copia de ellos en cada llamada a la función.

Por eso, yo en vez de

procedure mediana (cantidad: byte; orden:listado; var resultado:real);

haría

function mediana (cantidad: byte; orden:listado): real






(No se puede continuar esta discusión porque tiene más de dos meses de antigüedad. Si tienes dudas parecidas, abre un nuevo hilo.)