[PowerShell] Unicode + rzutowanie

0

Sto lat już nie zadawałem pytania na 4p :) Tym bardziej w tym dziale ;] (przeniosłem, bo brak odpowiedzi oznacza, że to raczej nic prostego)
Do rzeczy

$f = New-Object System.IO.StreamReader "jakis plik", Encoding.Unicode
$f.ReadLine() //wyświetla dane w kodowaniu ASCII (znak,\0,znak,\0,...)
$f.Close()

Jak otworzyć plik jako Unicode?
Co więcej, to też przechodzi:

$f = New-Object System.IO.StreamReader "jakis plik", adasdadasdas

RegExp łapię datę z tego pliku (do testów zapisany jako ASCII) i chcę ją przeformatować. Ponieważ miesiąc zapisany jest bez wiodącego zera, chcę je dodać. "{0,2}" uzupełnia puste miejsca spacjami, a mi potrzeba w tym miejscu zer, więc używam "{0:D2}", jednak to wymaga argumentu w postaci liczby - nie stringa. Potrzebna jest więc dodatkowa konwersja.
Robię to tak:

$smsDate = "{0}-{1:D2}-{2:D2} {3}_{4}_{5} " -f $match.Groups.Item("year"), [int]$match.Groups.Item("month"), [int]$match.Groups.Item("day"), $match.Groups.Item("hour"), $match.Groups.Item("minute"), $match.Groups.Item("second")

I dostaję błąd:

Cannot convert "8" to "System.Int32".

:|
Dodam, że takie coś działa:

"{0:D2}" -f [int]"8"

Co jest skopane?

0

mógłbyś wkleić output z komend ?

  1. $outputencoding

0

Oto i wynik

PS C:\Documents and Settings\Marooned> $outputencoding


IsSingleByte      : True
BodyName          : us-ascii
EncodingName      : US-ASCII
HeaderName        : us-ascii
WebName           : us-ascii
WindowsCodePage   : 1252
IsBrowserDisplay  : False
IsBrowserSave     : False
IsMailNewsDisplay : True
IsMailNewsSave    : True
EncoderFallback   : System.Text.EncoderReplacementFallback
DecoderFallback   : System.Text.DecoderReplacementFallback
IsReadOnly        : True
CodePage          : 20127



PS C:\Documents and Settings\Marooned> [Console]::OutputEncoding


IsSingleByte      : True
BodyName          : ibm852
EncodingName      : Central European (DOS)
HeaderName        : ibm852
WebName           : ibm852
WindowsCodePage   : 1250
IsBrowserDisplay  : True
IsBrowserSave     : True
IsMailNewsDisplay : False
IsMailNewsSave    : False
EncoderFallback   : System.Text.InternalEncoderBestFitFallback
DecoderFallback   : System.Text.InternalDecoderBestFitFallback
IsReadOnly        : True
CodePage          : 852
0

jeśli Twój skrypt składa się tylko z tych 3 linii:

$f = New-Object System.IO.StreamReader "jakis plik", Encoding.Unicode
$f.ReadLine()
$f.Close()

ma za zadanie wyświetlić zawartość i wyjście nie jest używane jako input dla jakichś starych tooli linii poleceń to nie wiem.
U mnie działa :)

Spróbuj z ciekawości przed odpalenie tego wykonać najpierw:

$OutputEncoding = [Console]::OutputEncoding

choć wątpię żeby to coś zmieniło.

0

Cały skrypt wygląda jak poniżej (dodaje datę na początku plików zawierające SMSy z Nokii z Symbianem):

# TODO
# open file as unicode
# padding '0' in date

$pattern_date = "Date:(?<day>\d{1,2})\.(?<month>\d{1,2})\.(?<year>\d{4}) (?<hour>\d{2}):(?<minute>\d{2}):(?<second>\d{2})"
$pattern_file = "\d{4}-\d{2}-\d{2} \d{2}_\d{2}_\d{2}"
#$pattern_file = "\d{4}- \d{1}-\d{2} \d{2}_\d{2}_\d{2}"
$regex_date = New-Object System.Text.RegularExpressions.Regex $pattern_date
$regex_file = New-Object System.Text.RegularExpressions.Regex $pattern_file
$count = $renamed = 0

foreach ($file in get-childitem *.vmg)
{
	$count++
	if ($regex_file.IsMatch($file.Name)) { continue }
	$f = New-Object System.IO.StreamReader $file.FullName
	do
	{
		$smsDate = $f.ReadLine()
		$match = $regex_date.Match($smsDate)
	} while (!($match.Success -and $match.Groups.Count -eq 7) -and $smsDate -ne "")
	$f.Close()
	if ($match.Success)
	{
		$smsDate = "{0}-{1:D2}-{2:D2} {3}_{4}_{5} " -f $match.Groups.Item("year"), [int]$match.Groups.Item("month"), [int]$match.Groups.Item("day"), $match.Groups.Item("hour"), $match.Groups.Item("minute"), $match.Groups.Item("second")
		"{0,-40} => {1}" -f $file.Name, ($smsDate + $file.Name)
		rename-item -path $file.FullName -newname ($smsDate + $file.Name)
		$renamed++
	}
}
"All files: {0}, renamed: {1}, skipped: {2}" -f $count, $renamed, ($count - $renamed)

Ale co do kodowania, to nawet robiłem w konsoli: otwarcie, wczytanie linii, wypisanie jej na ekran i.. już źle wyświetlało - przykładowo "123" w unicode jako "1a2a3a" gdzie 'a' jest wyświetlane zamiast \0 :|

0

mógłbyś gdzieś wrzucić jakiś przykładowy plik z sms (to zwykły plik tekstowy tyle że o innym rozszerzeniu?) ?

Ale co do kodowania, to nawet robiłem w konsoli: otwarcie, wczytanie linii, wypisanie jej na ekran i.. już źle wyświetlało - przykładowo "123" w unicode jako "1a2a3a" gdzie 'a' jest wyświetlane zamiast \0

jak zamieniałeś - coś w stylu $sr.ReadLine().Replace('\0', 'a') ?

0
PS C:\esy> $f = New-Object System.IO.StreamReader "desktop/esy/wyslany.vmg"
PS C:\esy> $f.ReadLine()
B E G I N : V M S G
PS C:\esy> $f.Close()

user image

przykładowy plik: http://marooned.4programmers.net/wyslany.vmg

0

nie potrafię Ci chyba pomóc :( Jak to się mówi, u mnie działa:

BEGIN:VMSG
VERSION:1.1
X-IRMC-STATUS:READ

jak Ci się wyświetla:

get-content "C:\\wyslany.vmg" -encoding Unicode

?

0

Hmm, no get-content wyrzuca elegancko. Bez -encoding tak jak powyżej, czyli 1a2a3a. Wynika z tego, że po prostu drugi parametr nie dochodzi do obiektu StreamReader, co można udowodnić, bo przechodzi "blabla" bez błędu. To może ja po prostu błędnie przekazuję te parametry?

No ok.. to mogę w takim razie użyć get-content w tym przypadku, ale pozostaje problem z rzutowaniem przez [int] :/ Może tu jakieś sugestie?

0

sugestia:



$pattern_date = "Date:(?<day>\d{1,2})\.(?<month>\d{1,2})\.(?<year>\d{4}) (?<hour>\d{2}):(?<minute>\d{2}):(?<second>\d{2})"
$pattern_file = "\d{4}-\d{2}-\d{2} \d{2}_\d{2}_\d{2}"

$regex_date = New-Object System.Text.RegularExpressions.Regex $pattern_date
$regex_file = New-Object System.Text.RegularExpressions.Regex $pattern_file

$count = $renamed = 0

foreach ($file in get-childitem *.vmg)
{
        $count++
        
        if ($regex_file.IsMatch($file.Name)) 
        {
			continue
		}

        foreach ($line in get-content $file -encoding Unicode)
        {		
			$match = $regex_date.Match($line)
			
			if ($match.Success) 
			{ 								
				$smsDate = "{0}-{1:D2}-{2:D2} {3}_{4}_{5} " -f $match.Groups.Item("year").value, [int]$match.Groups.Item("month").value, [int]$match.Groups.Item("day").value, $match.Groups.Item("hour").value, $match.Groups.Item("minute").value, $match.Groups.Item("second").value
				
				"{0,-40} => {1}" -f $file.Name, ($smsDate + $file.Name)
				
				rename-item -path $file.FullName -newname ($smsDate + $file.Name)
				
				$renamed++
				
				break
			}
		}		
}

"All files: {0}, renamed: {1}, skipped: {2}" -f $count, $renamed, ($count - $renamed)
0

.value ! ha :) dzięki, najciemniej pod latarnią...
a get-content zwraca całość, a nie linię, więc końcowa wersja wygląda tak :)

$pattern_date = "Date:(?<day>\d{1,2})\.(?<month>\d{1,2})\.(?<year>\d{4}) (?<hour>\d{2}):(?<minute>\d{2}):(?<second>\d{2})"
$pattern_file = "\d{4}-\d{2}-\d{2} \d{2}_\d{2}_\d{2}"

$regex_date = New-Object System.Text.RegularExpressions.Regex $pattern_date
$regex_file = New-Object System.Text.RegularExpressions.Regex $pattern_file

$count = $renamed = 0

foreach ($file in get-childitem *.vmg)
{
	$count++

	if ($regex_file.IsMatch($file.Name))
	{
		continue
	}

	$content = get-content $file -encoding Unicode
	$match = $regex_date.Match($content)
	if ($match.Success)
	{
		$smsDate = "{0}-{1:D2}-{2:D2} {3}_{4}_{5} " -f $match.Groups.Item("year").value, [int]$match.Groups.Item("month").value, [int]$match.Groups.Item("day").value, $match.Groups.Item("hour").value, $match.Groups.Item("minute").value, $match.Groups.Item("second").value
		"{0,-40} => {1}" -f $file.Name, ($smsDate + $file.Name)
		rename-item -path $file.FullName -newname ($smsDate + $file.Name)
		$renamed++
	}
}

"All files: {0}, renamed: {1}, skipped: {2}" -f $count, $renamed, ($count - $renamed)

Dzięki za pomoc!

0
Marooned napisał(a)

a get-content zwraca całość, a nie linię, więc końcowa wersja wygląda tak :)

zgadza się, dlatego w swojej wersji dałem:

foreach ($line in get-content $file -encoding Unicode)
        {               
                        $match = $regex_date.Match($line) [...]

grunt że działa

0

No działa, ale po co dodawać foreach, które zawsze ma 1 przebieg? :)
To tak, jakby kawałek kodu zamykać w if (true) {} - działa, ale kod zbędny :)

0
Marooned napisał(a)

No działa, ale po co dodawać foreach, które zawsze ma 1 przebieg? :)
To tak, jakby kawałek kodu zamykać w if (true) {} - działa, ale kod zbędny :)

albo jestem zmęczony albo nie rozumiem czegoś albo jedno i drugie :)

w mojej wersji foreach (z $line in get-content....) nie ma jednego przebiegu - get-content zwraca kolekcję stringów i następuje iterowanie po kolejnych liniach z pliku do momentu zmatchowania regexa.

wrzuć w środek
write-Host $line a skrypt wypluje wszystkie linie do napotkania daty.

dobrze mówię?

OK :) - jak mawiał Kasparov w swoich szachach - I see you point. Nie zwróciłem uwagi że w drugiej wersji wyciąłeś w ogóle foricza :)

0

No a ja też się przyznaję do błędu, bo byłem przekonany, że get-content zwraca całość jako jeden ciąg, a nie zestaw linii. Mea culpa :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1