Keskustelut - C/C++ - C ja Unix ongelma


iec 21:02 9.4.07 
Koulutyöhön liittyen tuli eteen sellainen ongelma, että pitäisi saada jollakin tekstieditorilla tehtyä sellainen tekstitiedosto ( vaikka a.txt) johon sitten on laitettu esimerkiksi 20 merkkiä ja se myös pitäisi näkyä 20 merkkinä. Ongelma on siis se, että esim pico-editorilla tehty tekstitiedosto näkyy 21 merkkisenä kun sitä käsittelee tekemälläni ohjelmalla. eli viimeinen merkki on tyhjä.

Vastaavaa ongelmaa ei esiinny windowsin puolella kun olen tehnyt tekstitiedoston Notepadillä. Se 20 merkkiä on myös 20 merkkiä mikä pitikin olla. Ongelma ei olisi varmaankaan kovin suuri jos en tekisi tätä koulutyötä etätehtävänä (jolloin voisin fyysisesti siirtää tekstitiedoston työasemalle) tai jos saisin tiedostonsiirron toimimaan jollakin tavalla tällä etäyhteydellä (mikä ei sekään näytä onnistuvan kirveelläkään).

Onko siis hyviä neuvoja millä tavoin voisin tehdä oikeanlaisen tekstitiedoston ilman ylimääräisiä merkkejä?

Ohessa pätkä kirjoittamaani koodia josta näkyy mihin tätä tarvitaan (olen todellakin aloittelija, joten olkaa armollisia):

 
 FILE *tiedosto;
     
     struct laskettavat a[251];
     struct laskettavat b[251];
   
     int i=0, j=0, k=0, m=0, n=0, p=0, q=0, r=0, pituus_a=0, pituus_b=0, alkio_a=0, alkio_b=0, ylinum_a=0, ylinum_b=0;
     int jj=0, check=0;
     char input_1[1002], input_2[1002], a1temp[5], b1temp[5], a2temp[5], b2temp[5], merkki_a[2], merkki_b[2];
     
     /* avataan ensimmäisen luvun tekstitiedosto a.txt lukemista varten */
    tiedosto = fopen("a.txt", "r");
    /* onnistuiko tiedoston avaaminen? */
    if (tiedosto != NULL)
       {                           
        while (!feof(tiedosto) && check!=1 ) {
            /* luetaan rivi (kuitenkin korkeintaan 1001 merkkiä*/
            fgets(input_1, sizeof(input_1), tiedosto);
           
            /* tulostetaan näytölle äsken luettu rivi */
            printf("\n%s", input_1);
            i=0;
            i=strlen(input_1);
            printf("\nMerkkijonon pituus on %i merkkiä.", i);
            if (i > 1001) {
                         printf("\nLiian paljon merkkejä!");
                         check=1;
                         }   
            if (i < 1) {
                         printf("\nLiian vähän merkkejä!");
                         check=1;
                         }
            /* luetaan muuttujaan merkki_a mahdollinen etumerkki ja tarkistetaan ovatko muut merkit numeroita*/
                                         
              sprintf(merkki_a, "%c", input_1[0]);
               
                       
              if (strchr(merkki_a,'-'))
                 {
                 check=0;
                 printf("\nmerkki a: %s", merkki_a);
                 for (j=0; j<=i-2; j++) {
                     input_1[j]= input_1[j+1]; /* ensimmäisen merkin (-) poisto muuttujasta input_1*/
                     }
                 input_1[i-1]='\0';   /* laitetaan viimeiseen merkkiin loppumerkki '\0'*/
                 
                 for (j=0; j<=i-2; j++) {
                       if (isdigit(input_1[j])){
                                    printf("\non numero!");
                                    check=0;
                                    }
                       else {
                            printf("\nVirhe merkissä: %d", j);
                            printf(" =>Ei ole numero!");
                            check=1;
                            break;
                            }
                            }
                  }
             
              else {
                   printf("\nEi etumerkkiä");
                   check=0;
                   sprintf(merkki_a, "+");
                   printf("\nmerkki a: %s", merkki_a);
                   for (j=0; j<=i-1; j++) {
                       if (isdigit(input_1[j])){
                                    printf("\non numero!");
                                    check=0;
                                    }
                       else {
                            printf("\nVirhe merkissä: %d", j);
                            printf(" =>Ei ole numero!");
                            check=1;
                            break;
                            }
                            }
                   }
           
            }
        fclose(tiedosto);
       
        }
     else
         {
         printf("\nTiedostoa a.txt ei löydy!");
         check=1;
         }


Luulisin, että virhe ei ole koodissa, mutta lisäsin sen tähän varmuuden vuoksi. Sain jollain ihmeen keinolla Unixin puolella tuosta a.txt -tiedostosta irti saman tiedon kuin mitä koodinpätkänikin kertoi Unixissa ajettuna, eli tiedosto tosiaankin näyttäisi olevan yhden merkin verran pitempi kuin on tarkoitus.

Kiitokset jo etukäteen,

-iec
editoitu: 21:06 9.4.07
Ztane 21:06 9.4.07 
ööh, nimen omaan. Sano opettajallesi, että tehtävänanto on perseestä, varsinkin kun unixeissa tekstitiedostojen pitää loppua \n:ään. (Windows on tässäkin asiassa rikki...)

Mutta no hätä, jos ei editointia tarvihe tehdä niin saat kyllä 20 merkkiä ilman rivinvaihtoa kirjoittamalla
echo -n "tähän sun kakskyt merkkiis" > a.txt


-n echolle kieltää useimmissa shelleissä laittamasta rivinvaihtoo loppuun.
iec 21:21 9.4.07 
Kiitokset Ztane! Todella nopea vastaus.

Heti toimi koodi, ja varmaan arvaatkin mihin koulutyöhön tämä liittyy. Tässä nimittäin yritetään laskea iiiiisoja numeroita +, -, * ja / .

Tämä rivinvaihtojuttu olisi kyllä saanut olla heti suoraan vinkkinä harjoitustyön kotisivulla imho. Tietysti siellä kyllä sanotaan että koodin pitää toimia unixissa ;)

-iec
feenix 21:44 9.4.07 
Ztane kirjoitti:
ööh, nimen omaan. Sano opettajallesi, että tehtävänanto on perseestä, varsinkin kun unixeissa tekstitiedostojen pitää loppua \n:ään. (Windows on tässäkin asiassa rikki...)


Millähän perusteella niiden pitää loppua \n:ään? En ole missään moista vaatimusta nähnyt, taitaa olla vain omaa keksintöäsi? Tekstitiedosto on vain binääritiedosto jota voi halutessaan käsitellä tekstinä.
editoitu: 22:15 9.4.07
Ztane 22:08 9.4.07 
öh, fakta vain on, että kaikki unix-editorit lisäävät rivinvaihdon loppuun, koska ilman rivinvaihtoa tiedosto ei toimi monissa tarkoituksissa.

EDIT: esmes C/C++-sorsatiedostot vaatii, että tiedosto loppuu rivinvaihtoon. Mikkisoftan IDE:t yms ihan kiltisti jättää laittamatta rivinvaihtoo loppuun ja särkee muita kääntäjiä.

"The ISO C standard, section 5.1.1.2, states that source files must end with
new lines."

ISO C++ standard: 2.1/1.2 "If a source file that is not empty does not end in a new-line
character, or ends in a new-line character immediately preceded by a
backslash character, the behavior is undefined".


Käytännössä mm tämän vuoksi kaikki unix-tekstieditorit oletuksena päättää tiedoston newlineen.
editoitu: 06:46 10.4.07
feenix 06:41 10.4.07 
Alkuperäiselle kysyjälle: jos kyseessä on nano, joka vain käynnistyy komennolla pico, anna sille parametri -L niin voit kirjoitella ilman ylimääräisiä rivinvaihtoja. Tai sitten ota joe tai jed, eivät myöskään lisää. Tai sitten teet cat > file, kirjoitat ne merkit ja painat Ctrl-D (mahdollisesti kahdesti). Tai sanot echo -n "asd" > tiedosto.



Ztane kirjoitti:
öh, fakta vain on, että kaikki unix-editorit lisäävät rivinvaihdon loppuun, koska ilman rivinvaihtoa tiedosto ei toimi monissa tarkoituksissa.


Esimerkiksi? Vaikkapa aluksi kymmenen tilannetta joissa ei "toimi"?

Ztane kirjoitti:
EDIT: esmes C/C++-sorsatiedostot vaatii, että tiedosto loppuu rivinvaihtoon. Mikkisoftan IDE:t yms ihan kiltisti jättää laittamatta rivinvaihtoo loppuun ja särkee muita kääntäjiä.


En ole vielä nähnyt yhtään kääntäjää joka ei toimisi vaikkei rivinvaihtoa olisi, valituksia niistä kylläkin saa. Tyhmää tietysti jättää laittamatta moinen loppuun (noissahan pyritään siihenkin että include olisi helppo, ei tarvitse kuin tuupata tiedostot peräkkäin), jostain syystä noita löytyy aika monesta OSS-projektista, Symbianin SDKista jne. Ja valituksia tulee kun kääntää mutta kaikki kääntyvät.

Myöskään kaikki tekstitiedostot eivät ole C/C++-kooditiedostoja, joten ei tuo päde kuin murto-osaan tekstitiedostoista. Ja minusta kyllä on ihan kilttiä että editori tekee sen mitä minä haluan, ei mitään muuta. Osaan nääs itse laittaa sen rivinvaihdon sinne. Ja templaateissa ne ovat. Anti-MS -asenteesi on vähän sama kuin valittaisi sorkkarautojen myyjille etteivät päällystä niitä kumilla ja nyt niitä voi käyttää ikkunoiden rikkomiseen...

Ztane kirjoitti:
Käytännössä mm tämän vuoksi kaikki unix-tekstieditorit oletuksena päättää tiedoston newlineen.


Kaikki? Meinaan jed ei lisää yhtään mitään. joe ei lisää mitään. Lienevät sitten jotain muita kuin editoreja tai järkyttävän rikki? Vakiona vi ja pico/nano lisää, mutta nanollekin voi antaa törkeän ruman option -L joka sanoo ettei lisätä loppuun rivinvaihtoa. Järkyttävää että moisia on tehty kun kerran sillä saadaan tiedostot rikki! Eclipsen editori ei lisäile mitään rivinvaihtoja, näin sitä rikotaan kääntäjiä!

Joku testannee loppuja editoreja ja vahvistaa, että kaikki lisäävät moisen kun tähän mennessä löytynyt vasta kolme yleistä jotka joko eivät lisää tai saa olemaan lisäämättä plus kaksi IDEä, joista toinen jopa ei-Microsoftin!

(Jep, on tylsää, mutta ei jaksa moisia vouhotuksia jotka ei perustu mihinkään faktaan)
editoitu: 09:00 10.4.07
Grez 09:00 10.4.07 
Eiköhän se jää käyttäjän vastuulle lopettaa tiedosto rivinvaihtoon jos käyttää editoria joka ei lisää sitä automaattisesti.

Yleisesti ottaen pidän huonona asiana, että peruseditori tekee automaattisesti jotain muutoksia tiedostoon, mutta ilmeisesti joidenkin Unix-käyttäjien mielestä asia on päinvastoin.
editoitu: 18:50 26.4.07
renni 18:50 26.4.07 
Ztane kirjoitti:
öh, fakta vain on, että kaikki unix-editorit lisäävät rivinvaihdon loppuun, koska ilman rivinvaihtoa tiedosto ei toimi monissa tarkoituksissa.

Fakta on että yksikään oikea unix-editori ei lisää mitään ylimääräisiä merkkejä minnekään. Ei myöskään rivinvaihtoja. Eri asia jos jossain tietyissä erikoistapauksissa ei toimi ilman, unixissa on kyllä outouksiaankin. Esim että jossain paikoissa välilyöntejä ei saa korvata tabeilla tai päinvastoin... Mutta ikinä yksikään oikea editori ei noita muuta puolestasi! Tiedoston koko voi olla vaikka 0 tavua ja sellaisena pysyy muokkauksen jälkeenkin.

Mako 02:11 27.4.07 
Se rivinvaihto tiedoston perässä johtunee sellaisesta asiasta, että unixeissa (tekstirivien) puskurointi pelaa pitkälle rivinvaihdoilla. Toki suurin osa unix-softista (ja käyttiksistä) toimii ihan hyvin ilman terminoivaa rivinvaihtoa eli siis streamin loppuminen aiheuttaa flushin.
weicco 07:06 27.4.07 
a new-line character


Määrittääkö C++ standardi rivinvaihdonkin? Tai onko jossain määritelty eksplisiittisesti "rivinvaihto = \n" (muussa kuin Unix spesifisessä dokumentissa)?
Jari_Kettunen 08:15 27.4.07 
weicco 09:30 27.4.07 
Huomaa boldattu kohta :)

An example of this is contained in the following code:

printf( "This\nis\na\ntest\n\nShe said, \"How are you?\"\n" );

which would display

...

Ei should, ei must, vaan konditionaali. Se ei sulje pois \r\n yhdistelmän käyttöä.
editoitu: 11:32 27.4.07
Jari_Kettunen 11:19 27.4.07 
Ihan ei selväksi käy mistä nyt kiistellään mutta c++ standardi ei ota kantaa varsinaiseen rakenteeseen se on vain tuottaa merkkijonoa.

Jos nyt puhutaan tekstitiedostoista niin ne päättyvät perinteisesti eof merkkiin (ascii 26). joka ei kuitenkaan ole mikään standardi ja sitä ei pidä sotkea eof-funktioon joka on luonnolliseti C++ standardissa.
Jos teet tiedoston windows-ympäristössä edlin-editorilla niin se tulee mutta ei esim. notepadilla.

Jos tekstitiedosto on rivitetty on sen merkki "line feed" (\n).

"carriage_return":n (\r) kuuluu erityisesti windowsympäristössä myös asiaan puhuttaessa esim. lähdekoodi tiedostoista. M$ notepad (ja jo edlin aiemmin) varmaan on syyllinen asiaan, koska se lisäsi automaattisesi riville molemmat merkit ja samaa käytäntöä on sitten jatkaneet muut windowseditorit. Asialle on perinteinen selitys

http://galaxy.ps.uci.edu/users/esirko/howto/crlf.html

kuitenkin tulostettaessa c-koodista "\n" se ei mitenkään ihmeellisesti windowsympäristössä muutu \r\n:ksi.

Periaate toimii edelleen samalla tavalla, asian oivaltaa parhaiten vaikka tekemällä yksinkertaisen tektititedoston vaikka notepadilla, sitten esim. vaikka seuraavalla hexaeditorilla vaihtaa 0D ja ja 0A arvoja ja command-promtista typettää tiedot näytölle tai kirjoittimelle.

http://www.chmaas.handshake.de/delphi/freeware/xvi32/xvi32.htm#download
Mako 18:12 28.4.07 
Niin, alun perin lienee kyse siitä, että mitä noiden merkkien pitäisi tehdä. CR (\r) palauttaa kursorin rivin alkuun molemmissa järjestelmissä, mutta joidenkin järjestelmien suunnittelijat ajattelivat, että LF (\n) vaan siirtää riviä alaspäin (palauttamatta kursoria rivin alkuun) ja toiset taas olivat sitä mieltä, että LF sekä siirtyy seuraavalle riville että rivin alkuun.
weicco 19:43 28.4.07 
CRLF tulee jonkin huhun mukaan wanhoista kirjoituskoneista, joka toimiikin juuri noin.